基于国密 SM3/SM4/SM2 的前后端数据完整性校验实战(附完整代码)

在政务、金融、电力等关键领域,数据在传输过程中不仅要保证机密性(不被窃取),还要保证完整性(不被篡改)。国密算法(SM2/SM3/SM4)作为国内标准,正被越来越多项目采用。本文将以一个真实项目中的前端代码为例,深入解析如何利用 SM2、SM4、SM3 实现一套完整的请求加密与数据完整性校验方案,并提供可直接复用的核心代码与实现思路。
一、为什么需要“加密+完整性校验”?
- 加密(SM4):保证数据不会被中间人看到,解决机密性问题。
- 完整性校验(SM3):保证数据在传输过程中没有被篡改,解决防篡改问题。
- 密钥安全交换(SM2):使用非对称加密安全传递对称密钥,避免密钥暴露。
三者结合,形成了一套端到端的安全通信机制,即使在 HTTPS 之外也能提供额外保护。
二、整体架构与数据流
前端 后端
1. 生成 SM4 密钥(key)和 IV
2. 用 SM2 公钥加密 key 和 IV → 放入请求头
3. 用 SM4 加密业务数据 → 得到密文 body
4. 用 SM3 计算密文的哈希 → 放入请求头
5. 生成 timestamp + nonce,SM3 签名 → 放入请求头
6. 发送请求(头 + 密文body)
7. 用 SM2 私钥解密出 SM4 密钥
8. 校验 timestamp 和 nonce(防重放)
9. 用 SM3 校验密文完整性
10. 用 SM4 解密得到业务数据
11. 处理业务,返回响应(同样加密+完整性)
12. 收到响应,校验响应完整性,解密得到业务数据
三、前端核心代码解析(基于 Axios 拦截器)
以下代码来自真实项目,我将逐段解释其核心逻辑。
3.1 国密库引入与配置
import {
sm4 as SM4 } from 'gm-crypt'
// 假设 smEncrypt 是一个封装了 SM2/SM3 的全局对象
const sm3 = smEncrypt.sm3
说明:
gm-crypt是 Node.js 环境下常用的 SM4 加密库;smEncrypt通常来自第三方国密 SDK,提供 SM2 加密和 SM3 哈希方法。实际开发中可根据技术栈选择对应的库(如sm-crypto)。
3.2 生成随机字符串与签名头(防重放)
/** 生成12位不重复随机字符串(用于 X-Nonce) */
const getUniqueNonce = () => {
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
const arr = $chars.split('')
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]]
}
return arr.slice(0, 12).join('')
}
/** 生成请求签名头 X-Timestamp、X-Nonce、X-Signature */
const getSignatureHeaders = () => {
const timestamp = Date.now().toString()
const nonce = getUniqueNonce()
const signature = sm3(timestamp + nonce) // SM3 签名
return {
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature
}
}
核心要点:
- 时间戳 + 随机数拼接后做 SM3 哈希,得到签名。
- 后端会校验时间戳是否在允许范围内(如5分钟),并检查 nonce 是否已被使用(需用 Redis 缓存),从而防止重放攻击。
3.3 动态生成 SM4 密钥与 IV,并用 SM2 公钥加密
const getAesKey = function (len = 32) {
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
const maxPos = $chars.length
let keyStr = ''
for (let i = 0; i < len; i++) {
keyStr += $chars.charAt(Math.floor(Math.random() * maxPos))
}
return keyStr
}
const aeskey = getAesKey().substring(0, 16) // SM4 密钥(16字节)
const aesiv =

&spm=1001.2101.3001.5002&articleId=159353122&d=1&t=3&u=ec8537e40ac0438ebe9658c34f80c29e)
366

被折叠的 条评论
为什么被折叠?



