NewHome/func/gmcodec.php

238 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* func/gmcodec.php —— 国密加解密
* SM2 非对称加解密 / SM3 摘要 / SM4 对称加解密。
* 说明SM1 为国密硬件算法(不公开、依赖专用芯片),无法在浏览器纯软件实现,故未提供。
*/
$P = '../';
require_once dirname(__DIR__) . '/includes/layout.php';
require_once __DIR__ . '/_bar.php';
layout_head('国密加解密');
func_bar('国密加解密');
?>
<main class="page-main">
<div class="wrap page-body">
<div class="panel-card">
<p class="tip" style="margin-top:0">支持 SM2 / SM3 / SM4 三种国密算法SM1 为不公开的硬件算法(需国密芯片),无法纯软件实现。
SM2/SM4 运算依赖在线加载 <code>sm-crypto</code> 组件(双 CDN加载失败会明确提示。</p>
<div class="field-row" style="align-items:center;margin-top:12px">
<label class="fl" style="margin:0" for="gmAlgo">算法</label>
<select id="gmAlgo">
<option value="sm4">SM4对称加解密</option>
<option value="sm2">SM2非对称加解密</option>
<option value="sm3">SM3摘要不可逆</option>
</select>
<div class="seg" id="gmDir">
<button type="button" data-dir="enc" class="active">加密</button>
<button type="button" data-dir="dec">解密</button>
</div>
<button type="button" class="btn btn-primary" id="gmRun">执行</button>
<button type="button" class="btn" id="gmSwap" title="把输出内容放入输入区">输出 → 输入</button>
<button type="button" class="btn" id="gmClear">清空</button>
</div>
<!-- SM4 参数区 -->
<div id="gmSm4">
<div class="field-row" style="margin-top:10px;align-items:center">
<label class="fl" style="margin:0">密钥16 字节)</label>
<input type="text" id="sm4Key" style="flex:1 1 240px;min-width:160px" placeholder="32 位十六进制,或恰好 16 个字符">
<button type="button" class="btn btn-sm" id="sm4KeyRand">随机密钥</button>
</div>
<div class="field-row" style="margin-top:8px;align-items:center">
<label class="fl" style="margin:0">模式</label>
<select id="sm4Mode">
<option value="ecb">ECB</option>
<option value="cbc">CBC</option>
</select>
<input type="text" id="sm4Iv" style="flex:1 1 240px;min-width:160px" placeholder="CBC 模式需 32 位十六进制 IV">
<button type="button" class="btn btn-sm" id="sm4IvRand">随机 IV</button>
<label class="fl" style="margin:0">密文格式</label>
<select id="sm4Out">
<option value="hex">十六进制</option>
<option value="b64">Base64</option>
</select>
</div>
</div>
<!-- SM2 参数区 -->
<div id="gmSm2" hidden>
<div class="field-row" style="margin-top:10px;align-items:center">
<label class="fl" style="margin:0">公钥</label>
<input type="text" id="sm2Pub" style="flex:1 1 320px;min-width:180px" placeholder="加密时填对方公钥130 位 hex">
<label class="fl" style="margin:0">私钥</label>
<input type="text" id="sm2Priv" style="flex:1 1 320px;min-width:180px" placeholder="解密时填自己的私钥130 位 hex">
<button type="button" class="btn btn-sm" id="sm2Gen">生成密钥对</button>
</div>
<p class="tip" style="margin:8px 0 0">SM2 为非对称算法:<strong>加密用“对方公钥”,解密用“自己私钥”</strong>;同一把私钥导出的公钥可直接用于加密。</p>
</div>
<!-- SM3 提示区 -->
<div id="gmSm3Tip" class="tip" hidden style="margin-top:8px">SM3 输出固定 64 位十六进制摘要HMAC 场景暂不提供,如需请使用后台维护的其它方案)。</div>
<div id="gmMsg"></div>
</div>
<div class="io-area">
<div class="io-box">
<div class="io-label">
<span>输入区(明文 / 待解密内容,可编辑)</span>
<button type="button" class="btn btn-sm js-copy" data-target="#gmIn">复制</button>
</div>
<textarea id="gmIn" placeholder="SM3任意文本SM4/SM2 加密UTF-8 明文解密粘贴密文十六进制SM4 亦支持 Base64"></textarea>
</div>
<div class="io-box">
<div class="io-label">
<span>输出区(可编辑)</span>
<button type="button" class="btn btn-sm js-copy" data-target="#gmOut">复制</button>
</div>
<textarea id="gmOut" placeholder="结果输出到这里,可手动修改后再次使用"></textarea>
</div>
</div>
</div>
</main>
<?php layout_theme_fab(); ?>
<?php layout_footer(); ?>
<script src="../assets/js/common.js"></script>
<script src="../assets/js/gmcrypto.js"></script>
<script>
(function () {
'use strict';
function $(id) { return document.getElementById(id); }
var algo = $('gmAlgo');
var dirButtons = Array.prototype.slice.call(document.querySelectorAll('#gmDir button'));
var gmIn = $('gmIn');
var gmOut = $('gmOut');
var gmMsg = $('gmMsg');
var panSm4 = $('gmSm4');
var panSm2 = $('gmSm2');
var panSm3Tip = $('gmSm3Tip');
var sm4IvRow = $('sm4Iv');
var ivRandBtn = $('sm4IvRand');
function activeDir() {
var b = dirButtons.filter(function (x) { return x.classList.contains('active'); })[0];
return b ? b.getAttribute('data-dir') : 'enc';
}
function setMsg(text, isErr) {
gmMsg.innerHTML = '';
if (!text) return;
var d = document.createElement('div');
d.className = isErr ? 'err-msg' : 'ok-msg';
d.textContent = text;
gmMsg.appendChild(d);
}
function syncUI() {
var v = algo.value;
panSm4.hidden = v !== 'sm4';
panSm2.hidden = v !== 'sm2';
panSm3Tip.hidden = v !== 'sm3';
var isHash = v === 'sm3';
dirButtons.forEach(function (b) {
b.disabled = isHash;
if (isHash) b.classList.remove('active');
});
if (!isHash && !dirButtons.some(function (x) { return x.classList.contains('active'); })) {
dirButtons[0].classList.add('active');
}
syncIv();
}
function syncIv() {
var cbc = $('sm4Mode').value === 'cbc';
sm4IvRow.disabled = !cbc;
ivRandBtn.disabled = !cbc;
}
function busy(on) {
$('gmRun').disabled = on;
$('gmRun').textContent = on ? '处理中…' : '执行';
}
function finish(okText) {
busy(false);
setMsg(okText, false);
}
function run() {
var text = gmIn.value;
if (!text.trim()) { setMsg('请先输入内容', true); gmIn.focus(); return; }
var v = algo.value;
var dir = activeDir();
var G = window.GmCrypto;
if (v === 'sm3') {
busy(true);
setMsg('正在计算 SM3 摘要…', false);
G.sm3(text).then(function (hex) {
gmOut.value = hex;
finish('SM3 摘要计算完成64 位十六进制)');
}).catch(function (e) { busy(false); setMsg('SM3 计算失败:' + (e && e.message ? e.message : e), true); });
return;
}
if (v === 'sm4') {
busy(true);
setMsg('正在执行 SM4 ' + (dir === 'enc' ? '加密' : '解密') + '…', false);
G.sm4(text, $('sm4Key').value, $('sm4Mode').value, $('sm4Iv').value, dir === 'enc')
.then(function (r) {
if (dir === 'enc') {
gmOut.value = ($('sm4Out').value === 'b64')
? G.bytesToB64(G.hexToBytes(r.hex))
: r.hex;
finish('SM4 加密完成');
} else {
gmOut.value = r.text || '';
finish('SM4 解密完成');
}
})
.catch(function (e) { busy(false); setMsg('SM4 ' + (dir === 'enc' ? '加密' : '解密') + '失败:' + (e && e.message ? e.message : e), true); });
return;
}
// SM2
busy(true);
setMsg('正在执行 SM2 ' + (dir === 'enc' ? '加密' : '解密') + '…', false);
var p = (dir === 'enc')
? G.sm2Encrypt(text, $('sm2Pub').value)
: G.sm2Decrypt(text, $('sm2Priv').value);
p.then(function (out) {
gmOut.value = out || '';
finish('SM2 ' + (dir === 'enc' ? '加密' : '解密') + '完成');
}).catch(function (e) { busy(false); setMsg('SM2 ' + (dir === 'enc' ? '加密' : '解密') + '失败:' + (e && e.message ? e.message : e), true); });
}
// 事件绑定
algo.addEventListener('change', syncUI);
dirButtons.forEach(function (b) {
b.addEventListener('click', function () {
if (b.disabled) return;
dirButtons.forEach(function (x) { x.classList.remove('active'); });
b.classList.add('active');
});
});
$('sm4Mode').addEventListener('change', syncIv);
$('sm4KeyRand').addEventListener('click', function () { $('sm4Key').value = window.GmCrypto.randomHex(16); });
ivRandBtn.addEventListener('click', function () { $('sm4Iv').value = window.GmCrypto.randomHex(16); });
$('sm2Gen').addEventListener('click', function () {
setMsg('正在生成 SM2 密钥对…', false);
window.GmCrypto.sm2Key().then(function (kp) {
$('sm2Pub').value = kp.publicKey;
$('sm2Priv').value = kp.privateKey;
setMsg('密钥对已生成,请妥善保存私钥后使用', false);
}).catch(function (e) { setMsg('密钥对生成失败:' + (e && e.message ? e.message : e), true); });
});
$('gmRun').addEventListener('click', run);
$('gmSwap').addEventListener('click', function () {
if (gmOut.value) { gmIn.value = gmOut.value; setMsg('已将输出内容回填到输入区', false); }
});
$('gmClear').addEventListener('click', function () {
gmIn.value = ''; gmOut.value = ''; setMsg('', false); gmIn.focus();
});
syncUI();
})();
</script>
</body>
</html>