import * as sjcl from 'sjcl-with-all' import { bindSM4 } from 'gmsm-sm4js' import { SM4 } from 'gm-crypto' // 绑定 SM4 到 sjcl bindSM4(sjcl) // 初始化 sjcl 的加密模式(这些模式在 sjcl.beware 中定义) if (sjcl.beware) { // 触发模式的初始化 Object.keys(sjcl.beware).forEach(key => { if (typeof sjcl.beware[key] === 'function') { sjcl.beware[key]() } }) } // 验证是否为有效的 hex 字符串 const isValidHex = (hex: string): boolean => { return /^[0-9a-fA-F]*$/.test(hex) && hex.length % 2 === 0 } // Hex 字符串转字节数组 const hexToBytes = (hex: string): number[] => { const bytes: number[] = [] for (let i = 0; i < hex.length; i += 2) { bytes.push(parseInt(hex.substr(i, 2), 16)) } return bytes } // 字节数组转 hex 字符串 const bytesToHex = (bytes: number[]): string => { return bytes.map(byte => byte.toString(16).padStart(2, '0')).join('') } // PKCS7 填充 const pkcs7Pad = (data: number[], blockSize: number): number[] => { const padLength = blockSize - (data.length % blockSize) const padded = [...data] for (let i = 0; i < padLength; i++) { padded.push(padLength) } return padded } // PKCS7 去填充 const pkcs7Unpad = (data: number[]): number[] => { if (data.length === 0) { return data } const padLength = data[data.length - 1] if (padLength > data.length) { return data } // 验证填充是否正确 for (let i = data.length - padLength; i < data.length; i++) { if (data[i] !== padLength) { return data } } return data.slice(0, data.length - padLength) } // SM4 加密 export const sm4Encrypt = (message: string, key: string, mode: string, iv?: string, paddingMode: 'pkcs7' | 'none' = 'pkcs7'): string => { // 验证消息是否为有效的 hex 字符串 if (!isValidHex(message)) { throw new Error('Invalid message: must be hex string') } // 验证密钥是否为有效的 hex 字符串 if (!isValidHex(key)) { throw new Error('Invalid key: must be hex string') } // 验证 IV 是否为有效的 hex 字符串(如果提供) if (iv && !isValidHex(iv)) { 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 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 ivBits = sjcl.codec.bytes.toBits(ivBytes) const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt) const encryptedBits = sjcl.mode.cbc.encrypt(sm4Key, dataBits, ivBits) const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits) return bytesToHex(encryptedBytes) } else if (modeLower === 'gcm') { // GCM 模式使用 gmsm-sm4js if (!iv) { 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 ivBits = sjcl.codec.bytes.toBits(ivBytes) const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt) // 传递标签长度参数(128 位) const encryptedResult = sjcl.mode.gcm.encrypt(sm4Key, dataBits, ivBits, sjcl.codec.bytes.toBits([]), 128) // GCM 模式返回的数据包含 tag if (!encryptedResult) { throw new Error('GCM encryption failed: result is undefined') } // 检查 encryptedResult 的类型 if (Array.isArray(encryptedResult)) { // 如果返回的是数组,直接使用(包含了 tag) const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedResult) return bytesToHex(encryptedBytes) } else if (encryptedResult.data) { // 如果返回的是对象,使用 data 属性(可能已经包含了 tag) const encryptedBits = encryptedResult.data const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits) return bytesToHex(encryptedBytes) } else { throw new Error('GCM encryption failed: unexpected result format') } } else { throw new Error(`Unsupported mode: ${mode}`) } } // SM4 解密 export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: string, paddingMode: 'pkcs7' | 'none' = 'pkcs7'): string => { // 验证加密数据是否为有效的 hex 字符串 if (!isValidHex(encrypted)) { throw new Error('Invalid encrypted data: must be hex string') } // 验证密钥是否为有效的 hex 字符串 if (!isValidHex(key)) { throw new Error('Invalid key: must be hex string') } // 验证 IV 是否为有效的 hex 字符串(如果提供) if (iv && !isValidHex(iv)) { 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 keyBytes = hexToBytes(key) // 创建 SM4 密钥 const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes)) const ivBytes = hexToBytes(iv) const ivBits = sjcl.codec.bytes.toBits(ivBytes) const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes) const decryptedBits = sjcl.mode.cbc.decrypt(sm4Key, encryptedBits, ivBits) const decryptedBytes = sjcl.codec.bytes.fromBits(decryptedBits) // 处理去填充 if (paddingMode === 'pkcs7') { return bytesToHex(pkcs7Unpad(decryptedBytes)) } return bytesToHex(decryptedBytes) } else if (modeLower === 'gcm') { // GCM 模式使用 gmsm-sm4js if (!iv) { 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 ivBits = sjcl.codec.bytes.toBits(ivBytes) const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes) // 传递标签长度参数(128 位) const decryptedResult = sjcl.mode.gcm.decrypt(sm4Key, encryptedBits, ivBits, sjcl.codec.bytes.toBits([]), 128) // 检查 decryptedResult 的类型 if (!decryptedResult) { throw new Error('GCM decryption failed: result is undefined') } if (Array.isArray(decryptedResult)) { // 如果返回的是数组,直接使用 const decryptedBytes = sjcl.codec.bytes.fromBits(decryptedResult) // 处理去填充 if (paddingMode === 'pkcs7') { return bytesToHex(pkcs7Unpad(decryptedBytes)) } return bytesToHex(decryptedBytes) } else if (decryptedResult.data) { // 如果返回的是对象,使用 data 属性 const decryptedBits = decryptedResult.data const decryptedBytes = sjcl.codec.bytes.fromBits(decryptedBits) // 处理去填充 if (paddingMode === 'pkcs7') { return bytesToHex(pkcs7Unpad(decryptedBytes)) } return bytesToHex(decryptedBytes) } else { throw new Error('GCM decryption failed: unexpected result format') } } else { throw new Error(`Unsupported mode: ${mode}`) } }