第3章 国密算法体系:SM2/SM3/SM4/SM9算法原理简介
做TLS协议的人,绕不开国密。说白了,国密就是咱们自己的一套密码算法标准。我当年刚接触时还以为就是把AES、RSA换了个名字,后来一研究才发现——嗯,门道不少。这章我带你把SM2、SM3、SM4、SM9这四兄弟捋一遍,搞懂它们是什么、干什么用、怎么配合。
3.1 国密家族总览
先看个总表,心里有个框架:
| 算法 | 类型 | 密钥长度 | 主要用途 | 性能特点(我实测感受) |
|---|---|---|---|---|
| SM2 | 椭圆曲线公钥密码 | 256位 | 数字签名、密钥交换、加密 | 签名比RSA快不少,验签稍慢 |
| SM3 | 密码杂凑函数 | 256位输出 | 消息摘要、HMAC、证书摘要 | 与SHA-256速度接近,很稳 |
| SM4 | 对称分组密码 | 128位 | 数据加密(TLS记录层主力) | 硬件实现效率极高,软实现比AES略低 |
| SM9 | 标识密码 | 256位(双线性对) | 无证书签名、密钥封装 | 计算开销较大,适合特定场景 |
注意看,SM2和SM9都基于椭圆曲线,但SM9用了双线性对,算起来重一些。我在做国密TLS网关时,一开始想用SM9做密钥交换,结果压测时CPU扛不住,后来改用SM2的ECDHE方案,才把性能拉回来。
3.2 SM2 —— 椭圆曲线公钥密码
SM2是国家密码管理局主推的商用公钥算法。它用的是256位的素域椭圆曲线,安全性相当于RSA 3072,但密钥短很多。
SM2有三个主要功能:数字签名、密钥交换、公钥加密。在TLS里面,最常见的是签名——用来给证书背书,或者做ECDHE的密钥交换。我记得有一次调试国密浏览器,发现签名验签总失败,查了半天是签名值编码顺序不对:SM2要求r在前、s在后,而OpenSSL默认的顺序是反的。坑深着呢。
// 简化的SM2签名流程(伪代码)
function sm2_sign(private_key, message):
e = sm3_hash(message) // 先算消息摘要
k = generate_random_number()
(x1, y1) = k * G // 点乘运算
r = (e + x1) mod n
s = (k - r * d) / (1 + d) mod n // 关键公式
return (r, s) // 签名对
3.3 SM3 —— 密码杂凑函数
SM3是国密体系的“万金油”。输入任意长度消息,输出256位摘要。它的结构类似SHA-256,但消息扩展和压缩函数做了定制。我实测过,速度跟SHA-256基本持平,但有些国产芯片还专门做了SM3硬件加速指令,那速度就快很多了。
在TLS里,SM3主要干两件事:
- 做证书签名的摘要(SM2签名前先对消息算SM3)
- 做TLS 1.3握手过程中的密钥派生(HKDF-Extract/Expand 里的底层哈希)
你想想看,如果SM3被碰撞了,整个TLS握手就崩了。目前SM3没有被攻击的风险,但我在项目里建议团队用SHA-256和SM3双摘要做证书签名的“双重保险”——虽然标准没要求,但多一层保护对密钥边界总没坏处。
// SM3压缩函数一步(伪代码)
for j from 0 to 63:
SS1 = ROTL((ROTL(A,12) + E + ROTL(T_j, j)), 7)
SS2 = SS1 ^ ROTL(A,12)
TT1 = FF_j(A,B,C) + D + SS2 + W'_j
TT2 = GG_j(E,F,G) + H + SS1 + W_j
// 更新8个工作寄存器...
3.4 SM4 —— 对称分组密码
SM4是国密体系里用的最多的对称算法。分组长度128位,密钥长度128位,采用Feistel结构,轮数32轮。说实话,我一开始觉得32轮太保守了,后来看到SSE指令集上SM4的轮函数可以并行化,才明白设计者的深意。
在TLS中,SM4当对称加密算法用,比如TLS 1.3的CIPHER SUITE: TLS_SM4_GCM_SM3。它用GCM模式,既加密又认证,一次搞定。我的经验是,SM4的密钥派生阶段要注意字节序:国密标准用大端,有些底层加密库用的小端,混在一起就麻烦了。
// SM4加密一个数据块(伪代码示意)
uint32_t rk[32]; // 轮密钥
uint32_t X[4]; // 初始明文块分为4个字
for i from 0 to 31:
uint32_t B = X[1] ^ X[2] ^ X[3] ^ rk[i];
uint32_t S = sbox_lookup(B); // 非线性S盒替换
uint32_t L = L_transform(S); // 线性置换
X = (X[0] ^ L, X[1], X[2], X[3]) rotate;
// 最后反序输出密文
3.5 SM9 —— 标识密码
SM9是一个有点“未来感”的算法。它不需要证书,直接用用户的标识(比如邮箱、手机号)当作公钥。私钥由密钥生成中心(KGC)通过主密钥派生。底层用了256位的BN曲线(Barreto-Naehrig),双线性对运算开销大。
在TLS里面,SM9主要用于两种场景:
- 无证书签名:客户端用自己标识签名,省了证书链验证的开销。
- 密钥封装:服务端用接收方标识封装会话密钥,接收方用私钥解开。
不过我得说实话,SM9的计算量比SM2大一个数量级。我曾在国产FPGA上用VHDL实现过SM9的双线性配对,算了整整一个下午才调通。在实际网络里,如果每秒握手1000次,用SM9做密钥封装CPU会爆。所以我一般在IoT或低并发场景才推荐用SM9,高并发场景还是老老实实SM2+SM4组合。
3.6 国密算法在TLS握手中的配合
好,现在我们把四个算法串起来。下图展示了我最常用的一个国密TLS 1.3握手流程中算法的位置:
这张图你看懂了吗?从左到右:先SM2签名证书验身份,然后SM3派生密钥,最后SM4-GCM加密隧道。SM9如果上场,会替换掉SM2的部分来做无证书协商——但实际部署中我很少见到完整的SM9 TLS场景,大部分厂商还是走SM2+SM4组合。
3.7 一点选型建议
好了,四个算法原理讲完了。最后给你一个实打实的建议:
- 如果做国密TLS网关,优先选SM2签名 + SM2 ECDHE + SM4-GCM + SM3。这个组合最成熟,性能和兼容性都最好。
- 如果做LoRa、NB-IoT等极低带宽场景,可以尝试SM9密钥封装,一次交互就能建立对称密钥,省掉证书传递的开销。
- 不管怎么选,一定要用国密合规的硬件安全模块(HSM)或安全芯片保存私钥。我在某金融项目里见过直接把SM2私钥写在配置文件里的……后来被整改了。