国密算法概述:SM2、SM3、SM4、SM9 算法原理与规范

说到国密,很多做对接的同学第一反应是「头疼」。其实说白了,国密就是一套我们自己写的密码算法标准,跟国际上的 RSA、AES、SHA-256 是同一个赛道的选手。我刚开始接触时也觉得跟 RSA 差不多,直到被 SM2 的签名格式卡了整整一下午。嗯,从那以后我学乖了——先弄清楚这些算法到底长什么样。

核心要点:国密不是黑盒,而是公开标准。搞懂 SM2、SM3、SM4、SM9 的原理和规范,是后面做兼容性测试的基础。

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

SM2 是非对称算法,对标国际上的 ECDSA / ECDH。它可以用在数字签名、密钥交换和公钥加密上。我个人习惯把 SM2 理解为「椭圆曲线版的 RSA」,但它的密钥长度更短、安全强度更高——256 位的 SM2 密钥等价于 3072 位的 RSA。

标准规范方面,你得记住这三个编号:

  • GB/T 32918.1-2016 — 总则,告诉你椭圆曲线怎么选
  • GB/T 32918.2-2016 — 数字签名算法,签名验签流程
  • GB/T 32918.4-2016 — 公钥加密算法,加密解密流程
避坑:我曾经遇到一个项目,接口文档里只写了「SM2 签名」,结果对方用的是 SM2 的加密算法来做签名。两者虽然算法相同,但数据格式和处理流程完全不同,调试了大半天才发现。

SM3:密码杂凑算法

SM3 是哈希算法,输出 256 位的摘要。它跟 SHA-256 长度一样,但算法细节不同。我在实际对接中见过最多的场景是:先做 SM3 摘要,再用 SM2 签名。说白了,SM3 的作用就是给任意长的数据「提取指纹」。

标准规范是 GB/T 32905-2016。你可能会问:直接用 SHA-256 不行吗?从算法本身来说不行——国密合规要求必须是 SM3。而且 SM3 的压缩函数的轮数设计使得它在软件实现上比 SHA-256 稍慢,但安全冗余更高。

特性 SM3 SHA-256
输出长度 256 bit 256 bit
消息分组 512 bit 512 bit
压缩轮数 64 轮 64 轮
消息扩展 132 字 64 字

嗯,这里要注意:SM3 的扩展算法和 SHA-256 完全不同,所以它们的输出值不可能一样。做兼容性测试时,必须确认双方用的都是 SM3,否则验签永远不会通过。

SM4:分组密码算法

SM4 是对称加密算法,跟 AES 对标。它的分组长度是 128 位,密钥长度也是 128 位。我觉得 SM4 比 SM2 简单得多——没有椭圆曲线,就是加解密。标准规范是 GB/T 32907-2016。

SM4 使用 Feistel 结构,跟我以前做过的 DES 有一点点像,但轮数更多(32 轮),安全性也高得多。它的 S 盒是国密自己设计的,不依赖任何国际标准。

技巧:你在对接接口时,SM4 一般会搭配 CBC / ECB 模式。我个人建议优先用 CBC,因为 ECB 模式下相同的明文块会得到相同的密文块,容易被分析攻击。

SM9:标识密码算法

SM9 是最新的国密公钥算法,跟 SM2 一样是非对称的,但有一个巨大的区别——它不需要证书。SM9 用「标识」来做公钥,比如用户的邮箱、手机号、QQ 号。标准规范是 GB/T 38635-2020。

你说这不就是 IBE(标识加密)吗?对,它就是基于双线性对的 IBE。我在项目中遇到过 SM9 的场景:两个系统要安全通信,但没有 CA 做证书。用 SM9 就直接拿对方的 ID 当公钥用,省掉了证书管理的麻烦。

四种算法的核心对比

算法 类型 密钥长度 主要用途 标准编号
SM2 非对称(椭圆曲线) 256 bit 签名 / 加密 / 密钥交换 GB/T 32918
SM3 哈希 256 bit(输出) 数据完整性 / 消息摘要 GB/T 32905
SM4 对称(分组密码) 128 bit 数据加密 GB/T 32907
SM9 非对称(标识密码) 256 bit 签名 / 加密(无证书) GB/T 38635

一个简单的示例:SM2 签名验签(Python)

我在写这个示例前,踩过一个大坑:SM2 的签名数据是 DER 编码的,很多人直接用原始 R/S 值拿去验签,结果通不过。下面是一个标准的签名流程:

from gmssl import sm2, func

# 生成密钥对
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429473747DB7D9F6C2E8B8F8C6C8A8B8F8C0A8F8E8D8'

sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)

# 待签名数据
data = b"hello, gmssl"

# 签名
sign = sm2_crypt.sign(data, random_hex=func.random_hex(32))
print("SM2 签名结果 (hex):", sign)

# 验签
verify = sm2_crypt.verify(sign, data)
print("验签通过:", verify)
警告:上面这个 private_key 是示例,不要在生产环境用。我记得有个同事把测试密钥直接部署上线了,结果被安全审计抓了个正着。

国密算法家族关系图

国密算法家族关系图 国密算法 SM2 SM3 SM4 SM9 非对称 / 签名加密 哈希 / 摘要 对称 / 加密 标识密码 / 无证书 注:SM9 基于 SM2 的椭圆曲线参数,但使用双线性对实现标识密码

你看,从结构上看,SM2 和 SM9 都属于公钥密码体系,但 SM9 不需要证书。SM3 是摘要算法,SM4 是对称加密。这四种算法覆盖了现代密码学的全部核心场景:加密、签名、摘要、密钥交换。

一个建议:刚开始做国密对接时,先确认对方用的是哪几种算法的组合。比如「SM2 签名 + SM3 摘要 + SM4 加密」就是最常见的套餐。别一上来就啃 SM9,除非接口明确要求。

标准规范速查表

我习惯把这几个标准编号贴在墙上,调试接口时直接翻:

  • SM2:GB/T 32918.1~5-2016
  • SM3:GB/T 32905-2016
  • SM4:GB/T 32907-2016
  • SM9:GB/T 38635.1~2-2020
  • 通用指导:GM/T 0009-2012(SM2 密钥格式)

说白了,这些标准文件才是第一手参考。网上很多博客连密钥长度都写错了,我建议你还是拿标准原文来对照。好,这一章我们先把算法的轮廓画清楚,后面章节再一个个拆开来细说。