第2章 对称加密算法SM4:从原理到实战

聊完分层架构的整体思路,咱们得先吃透最核心的加密算法。你在项目中用SM4,不能只会调个API——得知道它肚子里装的是什么。我个人习惯,讲算法一定从“它解决了什么问题”开始。

SM4,说白了就是国标里定义的对称分组密码算法。块大小128位,密钥长度128位,轮数32轮。你想想看,和AES-128相比,块和密钥尺寸一样,但轮数更多(AES只有10/12/14轮)。为什么?因为SM4的轮函数结构更轻量,靠轮数堆安全强度。

算法核心结构

SM4是Feistel网络结构,和一个叫“非平衡Feistel”的说法有关。不过别被名字吓到,它的加解密流程一模一样——只是轮密钥使用顺序相反。这在硬件实现里是个大优势,我当年做FPGA加速时就因为这特性省了不少逻辑门。

整个流程分为三个部分:

  • 密钥扩展:从128位主密钥生成32个32位轮密钥
  • 32轮迭代:每轮用一个轮密钥,做非线性变换和线性变换
  • 反向变换:最后把输出再做个简单重组得到密文

嗯,这里要注意:SM4的加解密只有轮密钥顺序不同,其他完全对称。解密时把轮密钥数组倒序使用就行。

我的踩坑经验:我曾经在项目里把SM4当成AES来做,直接用AES的ECB模式表,结果密钥扩展算出来全是错的。SM4的密钥扩展用了一个固定的系统参数FK和CK,和AES的Rcon完全不同。一定要查GB/T 32907-2016,别想当然。

轮函数拆解

每轮变换的核心叫F函数,输入四个字节X0~X3和一个轮密钥rk,输出一个字节。公式如下:

F(X0, X1, X2, X3, rk) = X0 ⊕ T(X1 ⊕ X2 ⊕ X3 ⊕ rk)

这里的T变换分成两步:非线性替换(S盒)和线性变换(L)。

S盒:查表就好

SM4的S盒和AES一样是8×8的,但值完全不同。254个元素,固定查表。不要自己去算,直接拿标准里的256字节数组。我见过有人试图用数学公式推导S盒,结果算错了一个字节,整个算法全废。别折腾,用现成的。

线性变换L

L是一个32位的循环移位+异或操作:

输出的32位B,经过L变换:

L(B) = B ⊕ (B <<< 2) ⊕ (B <<< 10) ⊕ (B <<< 18) ⊕ (B <<< 24)

这个设计让扩散性非常快。我记得做安全分析时,两轮之后每个输出位就与所有输入位相关了。

密钥扩展算法

主密钥MK分成MK0~MK3四个32位字。先和固定参数FK做异或:

K0 = MK0 ⊕ FK0
K1 = MK1 ⊕ FK1
K2 = MK2 ⊕ FK2
K3 = MK3 ⊕ FK3

然后循环生成32个轮密钥:

rki = Ki+4 = Ki ⊕ T'(Ki+1 ⊕ Ki+2 ⊕ Ki+3 ⊕ CKi)

这里的T'和加密的T结构一样,但线性部分换成了简单的移位:

T'(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)

CK是固定常数,每个轮次不同。我给你个快速检查方法:算出第一个轮密钥,如果和标准测试向量对不上,八成是CK顺序搞错了。

注意:SM4密钥扩展的S盒和加密的S盒是同一个!有些里分开写,但实际标准里用的是同一张表。我之前在审查别人代码时发现他们用了两个不同的S盒,结果解密出来的全是乱码。

加解密流程对比

步骤 加密 解密
输入 128位明文 128位密文
密钥扩展 rk0~rk31(顺序) rk31~rk0(反序)
轮操作 32轮F函数 32轮F函数
最后变换 将(X1,X2,X3,X0)重组为密文 将(X1,X2,X3,X0)重组为明文

你没看错,最后变换只是简单的字节重排。这个设计让硬件实现省了不少逻辑。

SM4相比AES的优劣势

  • 优势:加解密共用电路,节省面积;轮函数简单,适合低成本芯片;国密标准,国内合规必须用。
  • 劣势:软件实现速度通常比AES慢(因为32轮);国际认可度不如AES;S盒设计缺乏公开密码分析(虽然目前没发现漏洞)。
  • 我个人建议:如果只是国内合规项目,优先用SM4;如果涉及国际互通,用AES或者SM2+SM3做密钥协商后选AES。

一个实际案例:我在做物联网终端时,设备主控是ARM Cortex-M0,内存只有8KB。用SM4的ECB模式,密钥扩展预计算存入Flash,运行时只查表和移位,每一轮大概300个时钟周期,32轮总共不到1万周期,完全够用。比用AES的查表实现还快一点——因为SM4的S盒只有256字节,比AES的T表(4KB)省缓存。

SM4算法结构图

下面这张图把SM4的核心数据流画出来了。你看懂了它,就懂了SM4整个框架。

SM4 算法数据流 128位明文 (X0~X3) 密钥扩展 FK, CK, 32轮rk 128位主密钥MK 32轮迭代 (i=0..31) F(Xi, Xi+1, Xi+2, Xi+3, rki) rki S盒 (8×8) L变换 输出 Xi+4 = Xi ⊕ T(…) 下一轮 (i+1) 128位密文 (X32~X35) 最后一轮后 做反序变换 (X4,X3,X2,X1)→密文

从图里能清楚看到:主密钥走密钥扩展,生成32个轮密钥;明文进入32轮迭代,每轮用F函数做S盒和L变换;最后一轮做完再做个反序重组,就得到密文了。

实战中你需要注意的

  • 模式选择:SM4支持ECB、CBC、CTR、OFB、CFB等模式。我建议只要没兼容性限制,优先用CTR或CBC+随机IV。ECB有模式缺陷,别用了。
  • 填充方式:分组密码需要填充。PKCS7最常用。SM4标准里也推荐PKCS7。
  • 性能调优:如果软件实现,可以用预计算轮密钥查找表(类似AES的T表),把S盒和L变换合并成4个32位表,每轮从4次查表变成1次,速度能提升3~4倍。我有个项目靠这个将SM4吞吐从200Mbps提到800Mbps。
  • 安全考虑:SM4没有已知的旁路攻击漏洞?不,任何对称算法都要防侧信道。如果你的设备暴露在物理环境下,建议加掩码或者使用固定的延迟操作。我曾经审计过一个POS机固件,SM4时间常数变了,攻击者通过电磁分析恢复了部分轮密钥。

一句话总结:SM4就是一个128位Feistel网络,32轮,S盒+循环移位。记住密钥扩展用FK/CK,加解密只有密钥顺序不同。练习时拿官方测试向量(GB/T 32907附录A)调试,一次过就算真懂了。