临时去除 sm2

This commit is contained in:
cheney 2026-04-15 20:02:36 +08:00
parent 9be06035f4
commit 0ef0a78c38
5 changed files with 122 additions and 233 deletions

View File

@ -8,7 +8,8 @@
"build": "tsc && vite build", "build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview", "preview": "vite preview",
"test": "jest" "test": "jest",
"public": "tsc && vite build && surge dist seckit.surge.sh"
}, },
"dependencies": { "dependencies": {
"gm-crypto": "*", "gm-crypto": "*",

View File

@ -1,25 +1,19 @@
import { useState } from 'react' import { useState } from 'react'
import { generateSm2KeyPair, sm2Encrypt, sm2Decrypt } from './utils/algorithm/sm2'
import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3' import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3'
import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4' import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
import { hexToUtf8, utf8ToHex } from './utils/convert' import { hexToUtf8, utf8ToHex } from './utils/convert'
function App() { function App() {
const [activeTab, setActiveTab] = useState('sm2') const [activeTab, setActiveTab] = useState('sm3')
// SM2 状态
const [sm2PrivateKey, setSm2PrivateKey] = useState('')
const [sm2PublicKey, setSm2PublicKey] = useState('')
const [sm2Message, setSm2Message] = useState('')
const [sm2Signature, setSm2Signature] = useState('')
const [sm2Encrypted, setSm2Encrypted] = useState('')
const [sm2Decrypted, setSm2Decrypted] = useState('')
// SM3 状态 // SM3 状态
const [sm3Message, setSm3Message] = useState('') const [sm3Message, setSm3Message] = useState('')
const [sm3Result, setSm3Result] = useState('') const [sm3Result, setSm3Result] = useState('')
const [sm3MacKey, setSm3MacKey] = useState('') const [sm3HmacKey, setSm3HmacKey] = useState('')
const [sm3MacResult, setSm3MacResult] = useState('') const [sm3HmacResult, setSm3HmacResult] = useState('')
// SM4 状态 // SM4 状态
const [sm4Key, setSm4Key] = useState('') const [sm4Key, setSm4Key] = useState('')
@ -34,66 +28,7 @@ function App() {
const [hexInput, setHexInput] = useState('') const [hexInput, setHexInput] = useState('')
const [utf8Input, setUtf8Input] = useState('') const [utf8Input, setUtf8Input] = useState('')
// SM2 生成密钥对
const handleGenerateSm2KeyPair = () => {
try {
const { privateKey, publicKey } = generateSm2KeyPair()
setSm2PrivateKey(privateKey)
setSm2PublicKey(publicKey)
} catch (error) {
alert('生成密钥对失败: ' + error)
}
}
// SM2 从私钥计算公钥
const calculateSm2PublicKey = () => {
try {
// 简化处理,实际项目中可能需要使用正确的 API
alert('从私钥计算公钥功能需要正确的 API 支持')
} catch (error) {
alert('计算公钥失败: ' + error)
}
}
// SM2 签名
const signSm2 = () => {
try {
// 简化处理,实际项目中可能需要使用正确的 API
alert('签名功能需要正确的 API 支持')
} catch (error) {
alert('签名失败: ' + error)
}
}
// SM2 验签
const verifySm2 = () => {
try {
// 简化处理,实际项目中可能需要使用正确的 API
alert('验签功能需要正确的 API 支持')
} catch (error) {
alert('验签失败: ' + error)
}
}
// SM2 公钥加密
const handleEncryptSm2 = () => {
try {
const encrypted = sm2Encrypt(sm2Message, sm2PublicKey)
setSm2Encrypted(encrypted)
} catch (error) {
alert('加密失败: ' + (error instanceof Error ? error.message : error))
}
}
// SM2 私钥解密
const handleDecryptSm2 = () => {
try {
const decrypted = sm2Decrypt(sm2Encrypted, sm2PrivateKey)
setSm2Decrypted(decrypted)
} catch (error) {
alert('解密失败: ' + (error instanceof Error ? error.message : error))
}
}
// SM3 计算 // SM3 计算
const handleCalculateSm3 = () => { const handleCalculateSm3 = () => {
@ -105,13 +40,13 @@ function App() {
} }
} }
// SM3 MAC 计算 // SM3 HMAC 计算
const calculateSm3Mac = () => { const calculateSm3Hmac = () => {
try { try {
const result = sm3Hmac(sm3MacKey, sm3Message) const result = sm3Hmac(sm3HmacKey, sm3Message)
setSm3MacResult(result) setSm3HmacResult(result)
} catch (error) { } catch (error) {
alert('MAC 计算失败: ' + (error instanceof Error ? error.message : error)) alert('HMAC 计算失败: ' + (error instanceof Error ? error.message : error))
} }
} }
@ -160,12 +95,6 @@ function App() {
<h1></h1> <h1></h1>
<div className="tabs"> <div className="tabs">
<button
className={`tab ${activeTab === 'sm2' ? 'active' : ''}`}
onClick={() => setActiveTab('sm2')}
>
SM2
</button>
<button <button
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`} className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
onClick={() => setActiveTab('sm3')} onClick={() => setActiveTab('sm3')}
@ -186,81 +115,7 @@ function App() {
</button> </button>
</div> </div>
{/* SM2 面板 */}
{activeTab === 'sm2' && (
<div className="panel">
<h2>SM2 </h2>
<div className="form-group">
<label></label>
<textarea
value={sm2PrivateKey}
onChange={(e) => setSm2PrivateKey(e.target.value)}
placeholder="输入 SM2 私钥 (hex)"
/>
</div>
<div className="form-group">
<label></label>
<textarea
value={sm2PublicKey}
onChange={(e) => setSm2PublicKey(e.target.value)}
placeholder="输入 SM2 公钥 (hex)"
/>
</div>
<div className="button-group">
<button onClick={handleGenerateSm2KeyPair}></button>
<button onClick={calculateSm2PublicKey}></button>
</div>
<div className="form-group">
<label></label>
<textarea
value={sm2Message}
onChange={(e) => setSm2Message(e.target.value)}
placeholder="输入要签名或加密的消息"
/>
</div>
<div className="form-group">
<label></label>
<textarea
value={sm2Signature}
onChange={(e) => setSm2Signature(e.target.value)}
placeholder="输入签名结果"
/>
</div>
<div className="button-group">
<button onClick={signSm2}></button>
<button onClick={verifySm2}></button>
</div>
<div className="form-group">
<label></label>
<textarea
value={sm2Encrypted}
onChange={(e) => setSm2Encrypted(e.target.value)}
placeholder="输入加密结果"
/>
</div>
<div className="form-group">
<label></label>
<textarea
value={sm2Decrypted}
onChange={(e) => setSm2Decrypted(e.target.value)}
placeholder="解密结果将显示在这里"
/>
</div>
<div className="button-group">
<button onClick={handleEncryptSm2}></button>
<button onClick={handleDecryptSm2}></button>
</div>
</div>
)}
{/* SM3 面板 */} {/* SM3 面板 */}
{activeTab === 'sm3' && ( {activeTab === 'sm3' && (
@ -288,25 +143,25 @@ function App() {
<button onClick={handleCalculateSm3}> SM3</button> <button onClick={handleCalculateSm3}> SM3</button>
<div className="form-group" style={{ marginTop: '1.5rem' }}> <div className="form-group" style={{ marginTop: '1.5rem' }}>
<label>MAC </label> <label>HMAC </label>
<input <input
type="text" type="text"
value={sm3MacKey} value={sm3HmacKey}
onChange={(e) => setSm3MacKey(e.target.value)} onChange={(e) => setSm3HmacKey(e.target.value)}
placeholder="输入 MAC 密钥 (hex)" placeholder="输入 HMAC 密钥 (hex)"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
<label>SM3 MAC </label> <label>SM3 HMAC </label>
<textarea <textarea
value={sm3MacResult} value={sm3HmacResult}
readOnly readOnly
placeholder="SM3 MAC 计算结果将显示在这里" placeholder="SM3 HMAC 计算结果将显示在这里"
/> />
</div> </div>
<button onClick={calculateSm3Mac}> SM3 MAC</button> <button onClick={calculateSm3Hmac}> SM3 HMAC</button>
</div> </div>
)} )}
@ -338,7 +193,7 @@ function App() {
<div className="form-group"> <div className="form-group">
<label></label> <label></label>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}> <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
{['ecb', 'cbc', 'cfb', 'gcm'].map(mode => ( {['ecb', 'cbc', 'gcm'].map(mode => (
<button <button
key={mode} key={mode}
className={sm4Mode === mode ? 'primary' : ''} className={sm4Mode === mode ? 'primary' : ''}
@ -404,23 +259,27 @@ function App() {
<div className="panel"> <div className="panel">
<h2>Hex / UTF8 </h2> <h2>Hex / UTF8 </h2>
<div className="form-group"> <div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
<div className="form-group" style={{ flex: 1 }}>
<label>Hex </label> <label>Hex </label>
<textarea <textarea
value={hexInput} value={hexInput}
onChange={(e) => setHexInput(e.target.value)} onChange={(e) => setHexInput(e.target.value)}
placeholder="输入 hex 字符串" placeholder="输入 hex 字符串"
style={{ height: '200px' }}
/> />
</div> </div>
<div className="form-group"> <div className="form-group" style={{ flex: 1 }}>
<label>UTF8 </label> <label>UTF8 </label>
<textarea <textarea
value={utf8Input} value={utf8Input}
onChange={(e) => setUtf8Input(e.target.value)} onChange={(e) => setUtf8Input(e.target.value)}
placeholder="输入 UTF8 字符串" placeholder="输入 UTF8 字符串"
style={{ height: '200px' }}
/> />
</div> </div>
</div>
<div className="button-group"> <div className="button-group">
<button onClick={handleHexToUtf8}>Hex UTF8</button> <button onClick={handleHexToUtf8}>Hex UTF8</button>

10
src/types/gmsm-sm3js.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module 'gmsm-sm3js' {
export function sumHex(data: Uint8Array): string;
export function fromHex(hex: string): Uint8Array;
export function create(): any;
export function kdf(data: Uint8Array, len: number): Uint8Array;
export function sum(data: Uint8Array): Uint8Array;
export function toHex(bytes: Uint8Array): string;
export function normalizeInput(input: string | Buffer | Uint8Array): Uint8Array;
export function testSpeed(hashFn: Function, N: number, M: number): void;
}

View File

@ -32,10 +32,15 @@ export const sm3Digest = (message: string): string => {
// HMAC-SM3 计算 // HMAC-SM3 计算
export const sm3Hmac = (key: string, message: string): string => { export const sm3Hmac = (key: string, message: string): string => {
try { try {
// 验证 key 和 message 是否为有效的 hex 字符串 // 验证 key 是否为有效的 hex 字符串
if (!key) {
throw new Error('HMAC key is required')
}
if (!isValidHex(key)) { if (!isValidHex(key)) {
throw new Error('Invalid key: must be hex string') throw new Error('Invalid key: must be hex string')
} }
// 验证 message 是否为有效的 hex 字符串
if (!isValidHex(message)) { if (!isValidHex(message)) {
throw new Error('Invalid message: must be hex string') throw new Error('Invalid message: must be hex string')
} }
@ -47,6 +52,11 @@ export const sm3Hmac = (key: string, message: string): string => {
const keyBytes = fromHex(key) const keyBytes = fromHex(key)
const messageBytes = fromHex(message) const messageBytes = fromHex(message)
// 检查密钥长度
if (keyBytes.length === 0) {
throw new Error('HMAC key cannot be empty')
}
// 实现 HMAC-SM3 算法 // 实现 HMAC-SM3 算法
const blockSize = 64 // SM3 块大小为 512 位 = 64 字节 const blockSize = 64 // SM3 块大小为 512 位 = 64 字节

View File

@ -1,5 +1,6 @@
import * as sjcl from 'sjcl-with-all' import * as sjcl from 'sjcl-with-all'
import { bindSM4 } from 'gmsm-sm4js' import { bindSM4 } from 'gmsm-sm4js'
import { SM4 } from 'gm-crypto'
// 绑定 SM4 到 sjcl // 绑定 SM4 到 sjcl
bindSM4(sjcl) bindSM4(sjcl)
@ -76,6 +77,21 @@ export const sm4Encrypt = (message: string, key: string, mode: string, iv?: stri
throw new Error('Invalid IV: must be hex string') throw new Error('Invalid IV: must be hex string')
} }
// 根据模式选择加密方式
const modeLower = mode.toLowerCase()
if (modeLower === 'ecb') {
// ECB 模式使用 gm-crypto
return SM4.encrypt(message, key, {
mode: 'ecb',
inputEncoding: 'hex',
outputEncoding: 'hex',
padding: paddingMode === 'pkcs7' ? 'pkcs7' : 'none'
})
} else if (modeLower === 'cbc') {
// CBC 模式使用 gmsm-sm4js
if (!iv) {
throw new Error('IV is required for CBC mode')
}
// 转换为字节数组 // 转换为字节数组
const messageBytes = hexToBytes(message) const messageBytes = hexToBytes(message)
const keyBytes = hexToBytes(key) const keyBytes = hexToBytes(key)
@ -89,25 +105,6 @@ export const sm4Encrypt = (message: string, key: string, mode: string, iv?: stri
// 创建 SM4 密钥 // 创建 SM4 密钥
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes)) const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
// 根据模式选择加密方式
const modeLower = mode.toLowerCase()
if (modeLower === 'ecb') {
// ECB 模式
const blockSize = 16
const encryptedBytes: number[] = []
for (let i = 0; i < dataToEncrypt.length; i += blockSize) {
const block = dataToEncrypt.slice(i, i + blockSize)
const blockBits = sjcl.codec.bytes.toBits(block)
const encryptedBlockBits = sm4Key.encrypt(blockBits)
const encryptedBlockBytes = sjcl.codec.bytes.fromBits(encryptedBlockBits)
encryptedBytes.push(...encryptedBlockBytes)
}
return bytesToHex(encryptedBytes)
} else if (modeLower === 'cbc') {
// CBC 模式
if (!iv) {
throw new Error('IV is required for CBC mode')
}
const ivBytes = hexToBytes(iv) const ivBytes = hexToBytes(iv)
const ivBits = sjcl.codec.bytes.toBits(ivBytes) const ivBits = sjcl.codec.bytes.toBits(ivBytes)
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt) const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
@ -115,10 +112,23 @@ export const sm4Encrypt = (message: string, key: string, mode: string, iv?: stri
const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits) const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits)
return bytesToHex(encryptedBytes) return bytesToHex(encryptedBytes)
} else if (modeLower === 'gcm') { } else if (modeLower === 'gcm') {
// GCM 模式 // GCM 模式使用 gmsm-sm4js
if (!iv) { if (!iv) {
throw new Error('IV is required for GCM mode') throw new Error('IV is required for GCM mode')
} }
// 转换为字节数组
const messageBytes = hexToBytes(message)
const keyBytes = hexToBytes(key)
// 处理填充
let dataToEncrypt = messageBytes
if (paddingMode === 'pkcs7') {
dataToEncrypt = pkcs7Pad(messageBytes, 16)
}
// 创建 SM4 密钥
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
const ivBytes = hexToBytes(iv) const ivBytes = hexToBytes(iv)
const ivBits = sjcl.codec.bytes.toBits(ivBytes) const ivBits = sjcl.codec.bytes.toBits(ivBytes)
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt) const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
@ -161,6 +171,21 @@ export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: st
throw new Error('Invalid IV: must be hex string') throw new Error('Invalid IV: must be hex string')
} }
// 根据模式选择解密方式
const modeLower = mode.toLowerCase()
if (modeLower === 'ecb') {
// ECB 模式使用 gm-crypto
return SM4.decrypt(encrypted, key, {
mode: 'ecb',
inputEncoding: 'hex',
outputEncoding: 'hex',
padding: paddingMode === 'pkcs7' ? 'pkcs7' : 'none'
})
} else if (modeLower === 'cbc') {
// CBC 模式使用 gmsm-sm4js
if (!iv) {
throw new Error('IV is required for CBC mode')
}
// 转换为字节数组 // 转换为字节数组
const encryptedBytes = hexToBytes(encrypted) const encryptedBytes = hexToBytes(encrypted)
const keyBytes = hexToBytes(key) const keyBytes = hexToBytes(key)
@ -168,29 +193,6 @@ export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: st
// 创建 SM4 密钥 // 创建 SM4 密钥
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes)) const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
// 根据模式选择解密方式
const modeLower = mode.toLowerCase()
if (modeLower === 'ecb') {
// ECB 模式
const blockSize = 16
const decryptedBytes: number[] = []
for (let i = 0; i < encryptedBytes.length; i += blockSize) {
const block = encryptedBytes.slice(i, i + blockSize)
const blockBits = sjcl.codec.bytes.toBits(block)
const decryptedBlockBits = sm4Key.decrypt(blockBits)
const decryptedBlockBytes = sjcl.codec.bytes.fromBits(decryptedBlockBits)
decryptedBytes.push(...decryptedBlockBytes)
}
// 处理去填充
if (paddingMode === 'pkcs7') {
return bytesToHex(pkcs7Unpad(decryptedBytes))
}
return bytesToHex(decryptedBytes)
} else if (modeLower === 'cbc') {
// CBC 模式
if (!iv) {
throw new Error('IV is required for CBC mode')
}
const ivBytes = hexToBytes(iv) const ivBytes = hexToBytes(iv)
const ivBits = sjcl.codec.bytes.toBits(ivBytes) const ivBits = sjcl.codec.bytes.toBits(ivBytes)
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes) const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)
@ -202,10 +204,17 @@ export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: st
} }
return bytesToHex(decryptedBytes) return bytesToHex(decryptedBytes)
} else if (modeLower === 'gcm') { } else if (modeLower === 'gcm') {
// GCM 模式 // GCM 模式使用 gmsm-sm4js
if (!iv) { if (!iv) {
throw new Error('IV is required for GCM mode') throw new Error('IV is required for GCM mode')
} }
// 转换为字节数组
const encryptedBytes = hexToBytes(encrypted)
const keyBytes = hexToBytes(key)
// 创建 SM4 密钥
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
const ivBytes = hexToBytes(iv) const ivBytes = hexToBytes(iv)
const ivBits = sjcl.codec.bytes.toBits(ivBytes) const ivBits = sjcl.codec.bytes.toBits(ivBytes)
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes) const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)