3. SM3 密码杂凑算法:数字指纹的国产方案

密码杂凑算法,说白了就是给任意数据算出一个固定长度的“指纹”。这个指纹独一无二,数据改一点,指纹就天翻地覆。SM3 就是咱们国家自己的标准杂凑算法,在很多国密方案里,它都是身份认证的第一道关卡。

我个人习惯把 SM3 叫做“国标指纹机”。它输出的 256 位(32 字节)杂凑值,在身份认证场景里,就是用户的数字身份标识。你想想看,密码不能明文存,但存个指纹总没问题吧?

3.1 SM3 算法原理与流程

SM3 的结构其实和 SHA-256 很像,都是 Merkle-Damgård 结构。但细节上做了不少国产化调整。我拆成几个关键步骤来讲。

3.1.1 消息填充

任何长度的消息,先补位,再补长度。规则很简单:

  • 末尾先加一个 1 比特
  • 然后加一堆 0,直到长度模 512 等于 448
  • 最后附加 64 位的原始消息长度(大端序)

嗯,这里要注意:填充后的消息长度一定是 512 的整数倍。这样就能按 512 位(64 字节)一组来处理了。

3.1.2 迭代压缩

SM3 的核心是 64 轮压缩函数。每一轮用不同的常数和消息字,把 256 位的中间状态搅得面目全非。我画个流程图你就明白了。

SM3 压缩函数核心流程:

初始向量 IV → 消息分组扩展(W0~W67, W'0~W'63)→ 64 轮迭代压缩 → 输出 256 位杂凑值

SM3 算法整体流程图 SM3 密码杂凑算法流程图 原始消息 M 消息填充(补位+补长) 分组 B0, B1, ..., Bn-1 64 轮迭代压缩 256 位杂凑值 关键参数 • 分组大小:512 位 • 输出长度:256 位 • 轮数:64 轮

3.1.3 消息扩展

每一组 512 位的消息,会扩展成 132 个 32 位字:W0~W67 和 W'0~W'63。扩展算法用了异或、循环移位和置换,目的就是让消息位之间充分扩散。

我曾经在调试一个 SM3 实现时,发现扩展后的 W 值总是不对。查了半天,原来是循环移位写成了逻辑移位。嗯,这种细节坑过不少人。

3.2 SM3 与 SHA-256 的对比

很多人问我:SM3 和 SHA-256 到底有啥区别?我直接列个表,一目了然。

对比项 SM3 SHA-256
输出长度 256 位 256 位
分组大小 512 位 512 位
压缩轮数 64 轮 64 轮
消息扩展 W0~W67 + W'0~W'63 W0~W63
布尔函数 GG, FF(含异或、与、或) Ch, Maj(较简单)
循环移位 多种移位组合 固定右移
安全性 与 SHA-256 相当,部分指标略优 国际标准,广泛验证
国标合规 GB/T 32905-2016 无

我的经验:SM3 的布尔函数和移位设计更复杂,理论上抗碰撞能力略强于 SHA-256。但在实际工程中,两者的安全性差距可以忽略。选哪个,主要看合规要求。

3.3 SM3 在身份认证中的应用(数字指纹)

身份认证里,SM3 最常见的用法就是生成“数字指纹”。我举几个实际场景。

3.3.1 密码存储

明文存密码是找死。正确的做法是:

  1. 用户注册时,对密码做 SM3 杂凑,存杂凑值
  2. 用户登录时,同样做 SM3,比对杂凑值
  3. 为了防彩虹表,还要加盐(salt)

代码示例(伪代码):

// 注册
salt = random_bytes(16)
password_hash = SM3(salt + password)
store(username, salt, password_hash)

// 登录
salt, stored_hash = lookup(username)
input_hash = SM3(salt + input_password)
if input_hash == stored_hash:
    login_success()
else:
    login_fail()

避坑指南:我曾经见过一个项目,直接用 SM3(password) 不加盐。结果用户密码泄露后,攻击者用彩虹表一把全解出来了。记住:杂凑不等于安全存储,加盐是必须的。

3.3.2 消息完整性校验

在身份认证的挑战-响应协议里,SM3 用来保证消息没被篡改。流程是这样的:

  • 服务端生成随机数 challenge 发给客户端
  • 客户端计算 response = SM3(challenge + 共享密钥)
  • 服务端用同样的方式验证 response

你想想看,攻击者就算截获了 challenge 和 response,也无法反推出共享密钥。因为 SM3 是单向的。

3.3.3 数字证书指纹

国密证书(SM2 证书)里,CA 会对证书内容做 SM3 杂凑,然后用 SM2 签名。验证时,接收方先算证书的 SM3 杂凑,再用 CA 公钥验签。这个杂凑值就是证书的数字指纹。

我记得有一次帮客户排查证书链验证失败的问题,发现是中间证书的 SM3 指纹算错了——他们用了 SHA-256 来算国密证书的指纹。嗯,这种混用的情况在过渡期很常见。

3.4 小结

SM3 作为国标杂凑算法,在身份认证里扮演着“数字指纹”的角色。它的原理不复杂,但工程实现时要注意填充、扩展和加盐这些细节。和 SHA-256 比,SM3 更复杂一点,安全性相当,但合规性上不可替代。