密码算法基础:SM2、SM3、SM4 原理与应用
国密合规绕不开这三兄弟——SM2、SM3、SM4。说白了,它们就是国家替我们选好的密码工具箱。我做合规审计时经常发现,很多团队对它们一知半解,结果验收时被卡。这章我们就把它讲透。
国密算法全家桶
国密标准里,SM2 是公钥算法,SM3 是哈希算法,SM4 是对称加密。你想想看,一个安全的系统起码要干三件事:身份认证、数据完整性、数据保密。这三兄弟正好各司其职。
| 算法 | 类别 | 密钥长度 | 典型用途 |
|---|---|---|---|
| SM2 | 椭圆曲线公钥密码 | 256 位 | 数字签名、密钥交换、加密 |
| SM3 | 密码杂凑算法 | 256 位输出 | 数据完整性、数字证书摘要 |
| SM4 | 对称分组密码 | 128 位 | 数据加密、VPN、数据库加密 |
嗯,这个表你看一次就够了。咱们一个一个说。
SM2:椭圆曲线上的国标守卫
SM2 基于椭圆曲线,安全性等价于 2048 位 RSA。但密钥只有 256 位,性能好很多。我个人习惯场景都用 SM2 做签名,比 RSA 快一大截。
核心原理
椭圆曲线上的点加法构成循环群。SM2 用了素数域上的标准曲线——参数都是国家定好的,我们直接用就行。
重点:SM2 标准包含三个子功能——数字签名(SM2-1)、密钥交换(SM2-2)、公钥加密(SM2-3)。公钥加密部分较少用,因为对称加密更高效,但合规场景有要求就得上。
我在项目中遇到过一件尴尬事:一家合作方用的 SM2 签名长度是 64 字节,我们系统预留了 128 字节。结果后面字节全是 0x00,解析时被当成空数据。避坑指南:SM2 签名结构是 (r, s),各 32 字节,串在一起刚好 64 字节。接收端一定要按 DER 或简单拼接来解析,别自作聪明。
应用示例:用 OpenSSL 做 SM2 签名
# 生成 SM2 私钥
openssl ecparam -genkey -name SM2 -out sm2_private.pem
# 提取公钥
openssl ec -in sm2_private.pem -pubout -out sm2_public.pem
# 签名(数据文件 data.bin)
openssl dgst -sign sm2_private.pem -sha256 -out signature.bin data.bin
# 验签
openssl dgst -verify sm2_public.pem -sha256 -signature signature.bin data.bin
注意哈,OpenSSL 1.1.1 之后才原生支持 SM2。你要是用老版本,得打补丁或者换国密专用库——我当时就是卡在版本上,折腾了半天。
SM3:国密自己的 SHA-256
SM3 输出 256 位哈希值,结构和 SHA-256 很像,但消息扩展和压缩函数有差异。它在国密体系里地位很高,数字证书摘要、HMAC、区块链都会用到。
性能对比
| 算法 | 输出长度 | 安全强度 | 典型速度(软件) |
|---|---|---|---|
| SM3 | 256 位 | 128 位 | 约 500 MB/s |
| SHA-256 | 256 位 | 128 位 | 约 600 MB/s |
速度上 SM3 稍慢一点,但合规优先时这点差异可以忽略。我建议你直接把它当成国标哈希标准,签名、消息摘要、随机数生成都用它。
小技巧:SM3 的中间状态是 8 个 32 位字,如果你想做流式哈希(例如大文件分段计算),可以用标准库的 SM3_Init / SM3_Update / SM3_Final 接口。很多开源库都支持。
SM4:对称加密的国标担当
SM4 是 128 位分组密码,密钥长度也是 128 位。它和我们熟悉的 AES 一样,都是 Feistel 结构,但轮数更多——32 轮。为什么?因为设计时为了抵抗差分密码分析,多加了轮数。
工作模式
SM4 支持 ECB、CBC、CTR、GCM 等模式。我个人强烈建议不要用 ECB,它连同样的明文块都会得到同样的密文——太容易被猜。CBC 是需要填充的模式,CTR 和 GCM 是流式模式,不用填充。
- ECB:简单但危险。每个块独立加密,重复模式会暴露。
- CBC:需要 IV,加密是串行的,但不能并行解密?其实解密是可以并行的。但 IV 必须随机且不可重用。
- GCM:加上认证标签,能同时保证机密性和完整性。国密合规推荐模式。
注意:我做过一个数据库加密迁移,原来的程序用 SM4-ECB 存身份证号。因为身份证号前 6 位是地区码,很多纪录的前 6 位一样,结果密文看起来也有规律。这就是 ECB 的锅。后来改成 CBC,问题才解决。
代码示例:SM4 CBC 加解密(伪代码)
# 初始化上下文(GMSSL 或 Tongsuo)
ctx = SM4_CBC_Encrypt_init(key, iv)
plaintext = "需要保护的数据"
ciphertext = SM4_CBC_Encrypt_update(ctx, plaintext)
ciphertext += SM4_CBC_Encrypt_final(ctx)
# 解密
ctx = SM4_CBC_Decrypt_init(key, iv)
decrypted = SM4_CBC_Decrypt_update(ctx, ciphertext)
decrypted += SM4_CBC_Decrypt_final(ctx)
注意 IV 必须是 16 字节随机数。并且每次加密都要生成新的 IV,否则相同的密钥和 IV 会得到相同的密文。我踩过这个坑——测试环境忘了换 IV,生产上加密结果一模一样,还好没上线。
三剑客的协作关系
来看一个实际场景:你给用户发一份加密文件。流程是这样的——SM4 随机生成密钥加密文件,然后用接收方的 SM2 公钥加密那个 SM4 密钥。最后用 SM3 对整体做哈希,确保传输过程中没被篡改。三剑客一起上,安全感拉满。
你看,每个算法负责一个环节,缺一个都会有漏洞。合规检查时,专家会看你的密钥管理、算法选择、模式是否正确。我曾经遇到一个项目,明明用了 SM2 和 SM4,但密钥协商过程不符合国密标准——SM2 的密钥交换协议需要额外做身份校验。这种细节不注意,合规就过不了。
避坑指南
- SM2 的随机数必须好:如果随机数发生器弱,签名可能会泄露私钥。我见过用 C 库
rand()做 SM2 随机数的……简直吓人。 - SM3 不要截断:有些项目为了节省空间只取前 128 位哈希。虽然能工作,但安全强度直接降了一半。合规要求完整 256 位。
- SM4 的密钥不得硬编码:我审计时经常看到配置文件里写死 SM4 密钥。明文存储就是找死。密钥必须用硬件安全模块或密钥管理服务保护。
建议:引入国密算法时,优先选用通过国密局认证的商用密码产品(比如密码机、安全芯片)。自己实现的话,直接用 GMSSL 或 Tongsuo 这些开源实现,别手写。手写轮子十有八九有侧信道漏洞。