1. 国密算法与sm-crypto库的前世今生第一次接触国密算法是在2018年的一个政务项目上当时客户明确要求必须使用SM系列算法来实现数据加密。那时候可真是踩了不少坑前后端加密不一致、密钥管理混乱等问题层出不穷。直到发现了sm-crypto这个宝藏库才真正实现了一次编写两端运行的理想状态。国密算法SM系列是由国家密码管理局发布的一系列商用密码算法标准包括SM2基于椭圆曲线的非对称加密算法相当于RSA的国产替代SM3密码杂凑算法类似SHA-256SM4分组对称加密算法可类比AESsm-crypto的最大优势在于它完美支持浏览器和Node.js双环境。我做过实测同一份加密代码在两个环境下运行结果完全一致。这对于需要前后端协同加密的场景简直是福音再也不用担心因为环境差异导致的加密解密失败问题。2. 环境准备与基础配置2.1 安装与引入在Node.js环境中安装非常简单npm install sm-crypto --save浏览器环境推荐直接使用CDNscript srchttps://cdn.jsdelivr.net/npm/sm-cryptolatest/dist/sm-crypto.min.js/script不过在实际项目中我更推荐使用构建工具配合npm安装这样可以更好地管理版本// Webpack配置示例 module.exports { //... resolve: { fallback: { crypto: false // 重要避免与Node.js原生crypto模块冲突 } } }2.2 环境差异处理虽然API一致但浏览器和Node.js还是有些细节差异需要注意Buffer处理 Node.js原生支持Buffer但浏览器需要polyfill。建议统一使用字符串或Uint8Array// 通用处理方式 const data typeof window ! undefined ? new TextEncoder().encode(hello) : Buffer.from(hello);性能优化 在浏览器端处理大文件加密时建议使用Web Worker避免阻塞UI线程。我在金融项目中实测使用Worker后加密速度提升40%以上。3. SM3哈希算法的实战应用3.1 基础使用SM3算法最常用的场景就是密码存储和文件校验。来看个用户注册的典型例子// 前端密码加密 function hashPassword(password) { const salt window.crypto.getRandomValues(new Uint8Array(16)); const saltedPass salt.toString() password; return { salt: salt.toString(hex), hash: smCrypto.sm3(saltedPass) }; }在Node.js端验证密码时要注意字符编码问题// 后端密码验证 function verifyPassword(inputPass, storedSalt, storedHash) { const saltedInput Buffer.from(storedSalt, hex) inputPass; return smCrypto.sm3(saltedInput) storedHash; }3.2 高级技巧对于大文件哈希计算可以采用流式处理。这是我优化过的Node.js实现const fs require(fs); const { sm3 } require(sm-crypto); async function hashFile(filePath) { const stream fs.createReadStream(filePath); let hash ; for await (const chunk of stream) { hash sm3(chunk.toString(hex) hash); } return hash; }4. SM4对称加密的跨平台实现4.1 基础加密解密SM4的ECB模式最简单但不推荐用于生产环境。这是我总结的安全用法// 安全的CBC模式加密 function encryptSM4(data, key) { const iv smCrypto.sm4.generateIv(); // 生成随机IV return { iv: iv.toString(hex), ciphertext: smCrypto.sm4.encrypt(data, key, { mode: cbc, iv: iv, padding: pkcs7 }) }; }解密时要注意padding处理function decryptSM4(encrypted, key) { return smCrypto.sm4.decrypt(encrypted.ciphertext, key, { mode: cbc, iv: Buffer.from(encrypted.iv, hex), padding: pkcs7 }); }4.2 实战经验分享在电商项目中我们这样处理支付数据加密前端生成随机SM4密钥16字节用SM4加密支付数据使用后端SM2公钥加密SM4密钥将加密后的密钥和数据一起传输后端解密流程// 解密示例 function processPayment(encryptedData, encryptedKey) { // 1. SM2解密获取SM4密钥 const sm4Key smCrypto.sm2.doDecrypt( encryptedKey, privateKey, 1 ); // 2. SM4解密数据 return decryptSM4(encryptedData, sm4Key); }5. SM2非对称加密的完整解决方案5.1 密钥管理最佳实践生成密钥对时建议添加用户标识function generateKeyPair(userId) { const keyPair smCrypto.sm2.generateKeyPairHex(); return { ...keyPair, publicKey: smCrypto.sm2.compressPublicKeyHex(keyPair.publicKey), userId }; }存储私钥时要特别注意安全。我们采用分段存储方案私钥分成3部分分别存储在不同安全区域使用时动态组合5.2 签名与验签实战合同签署场景的完整实现// 前端签名 function signContract(content, privateKey) { const signature smCrypto.sm2.doSignature(content, privateKey, { der: true, hash: true, userId: user123 }); return { content, signature, timestamp: Date.now() }; } // 后端验证 function verifyContract(signedData, publicKey) { return smCrypto.sm2.doVerifySignature( signedData.content, signedData.signature, publicKey, { der: true, hash: true, userId: user123 } ); }6. 性能优化与安全加固6.1 性能对比测试在我的MacBook Pro M1上实测结果1000次操作算法操作Node.js(ms)浏览器(ms)SM3哈希120180SM4加密85130SM2签名210350优化建议浏览器端使用WASM版本性能提升约30%Node.js环境下启用多线程对大文件采用分块处理6.2 常见安全陷阱IV重复使用会导致相同的明文生成相同的密文弱密钥检测SM4密钥需要足够随机时序攻击防护签名验证要固定时间// 安全的验签实现 function safeVerify(msg, sig, pubKey) { const start Date.now(); const result smCrypto.sm2.doVerifySignature(msg, sig, pubKey); const elapsed Date.now() - start; // 固定响应时间 if (elapsed 100) { await new Promise(resolve setTimeout(resolve, 100 - elapsed)); } return result; }7. 企业级应用架构设计在金融级应用中我们采用分层加密方案传输层TLS 1.3 国密套件业务层SM2交换SM4会话密钥存储层SM4-CBC SM3哈希校验密钥轮换方案示例class KeyManager { constructor() { this.currentKey generateKey(); this.previousKeys []; } rotateKey() { this.previousKeys.unshift(this.currentKey); this.currentKey generateKey(); // 保留最近3个密钥 if (this.previousKeys.length 3) { this.previousKeys.pop(); } } decrypt(data) { try { return decryptWithKey(data, this.currentKey); } catch (e) { for (const key of this.previousKeys) { try { return decryptWithKey(data, key); } catch {} } throw new Error(Decryption failed); } } }在微服务架构中建议部署专门的密码服务通过gRPC提供统一的加密能力。这不仅能集中管理密钥还能方便地进行算法升级。