随着网络安全法和等保 2.0 的实施,国密算法(National Secret Algorithms)在金融、政务、物联网等领域的应用已成为合规的标配。本文将深入探讨 SM2、SM3、SM4 等核心算法的技术原理,对比国际标准算法,并提供详细的 Node.js 实战指南。

1. 国密算法背景

国密算法是由中国国家密码管理局发布的密码算法标准,旨在摆脱对国外密码技术的依赖,保障国家信息安全。

为什么需要国密?

  • 自主可控:避免算法后门和潜在的安全漏洞。
  • 合规要求:金融、政务等关键基础设施必须使用国密算法。
  • 性能优势:部分国密算法(如 SM2)在同等安全强度下,性能优于国际标准算法。

2. 核心算法深度解析

2.1 SM2:椭圆曲线公钥密码算法

SM2 是一种基于椭圆曲线密码(ECC)的非对称加密算法。

  • 原理:基于椭圆曲线离散对数问题(ECDLP)。
  • 密钥长度:推荐使用 256 位。
  • 安全性:SM2-256 的安全强度相当于 RSA-2048 到 RSA-3072 之间,但密钥长度远小于 RSA。
  • 功能
    • 数字签名:用于身份认证和数据完整性保护。
    • 密钥交换:用于通信双方协商会话密钥。
    • 公钥加密:用于数据加密传输。

SM2 vs RSA:

特性 SM2 (256位) RSA (2048位)
算法结构 ECC 大数分解
密钥生成速度 快 (100倍+)
加解密速度 解密慢
签名速度 验签快
密文长度

2.2 SM3:密码杂凑算法

SM3 是一种密码杂凑(Hash)算法,用于生成消息摘要。

  • 输出长度:256 位(32 字节)。
  • 结构:Merkle-Damgård 结构。
  • 安全性:抗碰撞性优于 MD5 和 SHA-1,与 SHA-256 相当,但设计上针对特定攻击进行了优化。

应用场景

  • 用户密码存储(加盐 Hash)
  • 文件完整性校验
  • 数字签名中的摘要生成

2.3 SM4:分组密码算法

SM4 是一种分组密码算法(对称加密),原名 SMS4,最初用于无线局域网 WAPI 标准。

  • 分组长度:128 位(16 字节)。
  • 密钥长度:128 位。
  • 轮数:32 轮非线性迭代。
  • 安全性:与 AES-128 相当。

SM4 vs AES:

特性 SM4 AES
密钥长度 128位 128/192/256位
分组长度 128位 128位
结构 非平衡 Feistel SP 网络
效率 软硬件实现均高效 极高 (有指令集支持)

3. 实战:Node.js 中使用 sm-crypto

我们将使用 sm-crypto 库来演示如何在 Node.js 环境中实现国密算法。

3.1 环境准备

1
npm install sm-crypto --save

3.2 SM2 非对称加密实战

SM2 加密时需要注意加密模式sm-crypto 支持 C1C3C2 (默认) 和 C1C2C3 两种模式。前端与后端交互时,务必确认使用的模式一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const sm2 = require('sm-crypto').sm2

// 1. 生成密钥对
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey // 公钥 (130位 hex)
let privateKey = keypair.privateKey // 私钥 (64位 hex)

console.log('公钥:', publicKey)
console.log('私钥:', privateKey)

// 2. 加密
const msgString = 'Hello, 国密!'
const cipherMode = 1 // 1 - C1C3C2 (推荐), 0 - C1C2C3
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode)
console.log('密文:', encryptData)

// 3. 解密
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode)
console.log('解密结果:', decryptData)

// 4. 签名与验签
// 签名 (默认纯签名,不带 userId)
let sigValue = sm2.doSignature(msgString, privateKey)
console.log('签名:', sigValue)

// 验签
let verifyResult = sm2.doVerifySignature(msgString, sigValue, publicKey)
console.log('验签结果:', verifyResult)

3.3 SM3 摘要计算实战

SM3 常用于计算数据的指纹。

1
2
3
4
5
6
const sm3 = require('sm-crypto').sm3

const msg = 'abc'
let hashData = sm3(msg)
console.log('SM3 Hash:', hashData)
// 输出: 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0

3.4 SM4 对称加密实战

SM4 适合对大量数据进行加密。注意密钥必须是 128 位(16 字节,即 32 位 hex 字符串)。

1
2
3
4
5
6
7
8
9
10
11
12
const sm4 = require('sm-crypto').sm4

const msg = '敏感数据内容'
const key = '0123456789abcdeffedcba9876543210' // 128位密钥

// 加密
let encryptData = sm4.encrypt(msg, key)
console.log('SM4 密文:', encryptData)

// 解密
let decryptData = sm4.decrypt(encryptData, key)
console.log('SM4 解密:', decryptData)

4. 常见问题与坑

  1. 密钥格式:SM2 公钥通常是 04 开头的 130 位 Hex 字符串(未压缩),私钥是 64 位 Hex。如果遇到 PEM 格式,需要进行转换。
  2. 加密模式:SM2 的密文排序有 C1C2C3 和 C1C3C2 两种标准。旧标准多用 C1C2C3,新标准(及 OpenSSL 1.1.1+)多用 C1C3C2。对接时一定要确认。
  3. 性能:在纯 JS 环境下,SM2 的性能可能不如原生 C++ 实现的模块。对于高并发场景,建议使用基于 OpenSSL 或 C++ 扩展的库(如 gm-crypto)。

5. 总结

掌握国密算法是国内开发者的必备技能。通过 sm-crypto,我们可以轻松地在 Node.js 和浏览器端实现 SM2/SM3/SM4。在实际项目中,建议结合 HTTPS 使用,构建多层纵深防御体系。