4. SM3 密码杂凑算法:从原理到实战

SM3 是国密标准里的杂凑算法,说白了就是给任意长度的数据算出一个固定长度的“指纹”。我最早接触它是在一个政务系统项目里,当时甲方明确要求必须用国密,不能用 SHA-256。嗯,从那以后我就把 SM3 的细节啃了个透。

这一章咱们就把它讲清楚:消息怎么填充、怎么迭代压缩、消息怎么扩展,再跟 SHA-256 做个对比,最后看看它在 HMAC 和区块链里怎么落地。

4.1 SM3 算法原理

SM3 的输出长度是 256 位,也就是 32 个字节。整个流程分三步:消息填充、迭代压缩、消息扩展。我习惯把这三步记成“填平、揉碎、再拧紧”。

4.1.1 消息填充

SM3 处理消息的最小单位是 512 位(64 字节)的分组。如果消息长度不是 512 的倍数,就得填充。

填充规则很简单:

  • 先在消息末尾补一个 1 比特(二进制 1)
  • 然后补 0,直到长度满足 (消息长度 + 填充长度 + 64) mod 512 = 0
  • 最后 64 位放原始消息的长度(大端序)

举个例子:原始消息是“abc”,长度 24 比特。填充后:

原始: 01100001 01100010 01100011
补1:  01100001 01100010 01100011 1
补0:  ... 后面补 0 直到长度 = 512 - 64 - 24 - 1 = 423 个 0
最后64位: 00000000...00011000 (24 的大端表示)
我的习惯:写代码时别自己算填充长度,直接用位运算判断。我曾经手算错过一次,调试了整整一下午才发现是填充长度少算了 1 位。

4.1.2 迭代压缩

填充完成后,消息被分成若干个 512 位的分组。每个分组经过 64 轮压缩,最终输出 256 位的杂凑值。

初始值 IV 是固定的 8 个 32 位字:

IV = 0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
     0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E

每一轮压缩函数 CF 接收当前分组和上一轮的 256 位状态,输出新的 256 位状态。64 轮结束后,把最终状态和初始 IV 异或,得到该分组的杂凑值。

核心公式(简化版):

V(i+1) = CF(V(i), B(i))

其中 V(0) = IV,B(i) 是第 i 个消息分组。

4.1.3 消息扩展

每个 512 位的分组需要扩展成 132 个 32 位字(W0 ~ W67 和 W'0 ~ W'63),供 64 轮压缩使用。

扩展规则:

  1. 前 16 个字 W0 ~ W15 直接从分组中取
  2. W16 ~ W67 通过异或和循环移位生成
  3. W'j = Wj XOR Wj+4

你想想看,为什么要扩展?说白了就是为了让每一轮的输入都跟其他轮次充分“搅和”在一起,避免碰撞。

4.2 与 SHA-256 的对比分析

SM3 和 SHA-256 都是 256 位输出,结构也很像。但细节上差别不小。我整理了一个对比表:

对比项 SM3 SHA-256
输出长度 256 位 256 位
分组大小 512 位 512 位
压缩轮数 64 轮 64 轮
消息扩展 132 个字 64 个字
布尔函数 GG, FF(含异或和与运算) Ch, Maj(较简单)
循环移位 多种移位组合 固定右移
安全性 抗碰撞性优于 SHA-256 已被理论攻击削弱

我个人觉得,SM3 在布尔函数和消息扩展上比 SHA-256 更复杂,所以安全性也更高。但代价就是性能稍慢——大概慢 10%~15%。不过现在硬件加速普及,这点差距基本可以忽略。

避坑指南:我曾经在一个项目中直接用 SHA-256 的代码改成 SM3,结果发现消息扩展部分完全不一样。SM3 的 W' 数组是 SHA-256 没有的,千万别偷懒直接改参数。

4.3 SM3 在 HMAC 中的应用

HMAC(基于哈希的消息认证码)需要底层杂凑算法。用 SM3 做 HMAC,流程跟 HMAC-SHA256 一样:

HMAC-SM3(K, M) = SM3( (K' XOR opad) || SM3( (K' XOR ipad) || M ) )

其中:

  • K' 是密钥经过填充后的 512 位值
  • ipad = 0x36 重复 64 次
  • opad = 0x5C 重复 64 次

我在做国密改造时,经常遇到的一个坑是:密钥长度超过 64 字节时,需要先用 SM3 对密钥做一次杂凑,再用杂凑值作为密钥。这个细节很多文档没写清楚。

我的建议:直接用 GM/T 0054 标准里的 HMAC-SM3 实现,别自己造轮子。标准里连测试向量都给了,省心。

4.4 SM3 在区块链中的应用

区块链里哈希算法是核心。比特币用 SHA-256,以太坊用 Keccak-256。国密区块链自然用 SM3。

主要用在三个地方:

  1. 交易哈希:每笔交易的唯一标识
  2. 区块哈希:区块头的指纹,链接前后区块
  3. Merkle 树:验证交易是否被篡改

我记得有个联盟链项目,客户要求所有交易哈希必须用 SM3。当时我们直接把底层哈希接口替换成 SM3,上层逻辑完全不用改。但要注意:SM3 的输出是 32 字节,跟 SHA-256 一样,所以数据结构兼容性很好。

下面这张图展示了 SM3 在区块链 Merkle 树中的角色:

SM3 在 Merkle 树中的应用 交易 A 交易 B 交易 C 交易 D SM3(A||B) SM3(C||D) Merkle 根 每个哈希节点都使用 SM3 算法计算,保证数据完整性

从图中可以看到,每个交易先单独算 SM3 哈希,然后两两拼接再算哈希,最终得到 Merkle 根。只要有一个交易被篡改,根哈希就会变。这就是区块链防篡改的基础。

关键点:SM3 在区块链里不只是算哈希,还参与数字签名(SM2)和密钥派生。三者配合才能构成完整的国密区块链安全体系。