SM4算法原理:分组密码结构、密钥扩展、轮函数、ECB/CBC/CTR模式

各位好,咱们今天聊 SM4 算法。这是国密标准里的对称密码,说白了就是加密和解密用同一把钥匙。我在项目中经常用它来保护通信数据,尤其是和硬件加密卡打交道时,SM4 的出镜率特别高。

很多人觉得对称密码无非就是分组、轮函数、密钥扩展,没什么新鲜的。嗯,你理解得没错,但具体到每个细节,还是有不少坑的。我刚开始用 SM4 时,就因为在 OFB 模式里初始化向量复用错了,导致整批密文出问题。所以咱还是从原理到模式,一步步捋清楚。

分组密码结构:Feistel 网络

SM4 用的是 Feistel 网络结构,这个结构挺有意思。它将 128 位明文分成左右两半,每轮只处理一半的数据,另一半原封不动地交换到下一轮。这样做的好处是加解密可以用完全相同的代码逻辑,只是密钥使用顺序相反而已。

  • 分组大小:固定 128 位
  • 密钥长度:也是 128 位
  • 轮数:32 轮,不少吧?这就保证了足够的安全性
  • 每轮结构:使用轮密钥对右半部分进行变换,结果与左半部分异或,然后左右交换

为什么分组大小必须是 128 位?我记得在早期的一些项目中,还在用 64 位分组的 DES,现在看那就是给攻击者留后门。128 位是目前对称密码的标配,兼顾了效率和安全性。

核心点:SM4 的 Feistel 结构让加解密实现变得非常简单。我在封装 SDK 时,直接用同一套函数处理加密和解密,只要把轮密钥序列反过来就行。这个设计真的很聪明。

密钥扩展:固定参数与循环移位

密钥扩展,说白了就是怎么把自己的 128 位密钥,变出 32 个子密钥来。SM4 的方法很巧妙,它使用了固定的系统参数和固定的常量。

具体步骤大概是:

  1. 把 128 位密钥分成 4 个 32 位字,依次记为 MK0、MK1、MK2、MK3
  2. 用系统参数 FK0、FK1、FK2、FK3 和上面四个字做异或,得到初始的 K0、K1、K2、K3
  3. 从第 4 轮开始,用固定的常量 CK 和循环左移操作,逐个生成 rk0 到 rk31

我的小技巧:在实际编码时,建议将轮密钥一次性扩展完,存到一个数组里。否则每轮都重新算一遍,性能会很难看。我曾在一个嵌入式项目里踩过这个坑,32 轮全部实时扩展,MCU 直接卡死在加解密上。

SM4 密钥扩展参数表
参数 值 说明
FK0 0xA3B1BAC6 系统参数
FK1 0x56AA3350 系统参数
FK2 0x677D9197 系统参数
FK3 0xB27022DC 系统参数
CK[i] 固定表 32 个常量,每个 32 位

轮函数:S 盒与线性变换

SM4 的轮函数包括三个关键操作:S 盒替换、线性变换 L 和轮密钥异或。你想想看,每一步都有自己的讲究。

  • S 盒:8 位输入、8 位输出,是一个固定的置换表。我个人习惯在代码里用预计算的数组
  • 线性变换 L:将 S 盒输出再次做循环移位和异或,增加扩散性
  • 轮密钥异或:将当前轮的子密钥与状态数据异或,这是 Feistel 网络里的常规操作

注意:SM4 的 S 盒是国标里规定死的,不允许任何人动。但 S 盒的设计其实很讲究,它有一定的代数性质,能抵抗差分分析和线性分析。

避坑指南:不要试图自作聪明去修改 S 盒!我曾经在实验阶段试过用自己的查找表,结果加密出来的结果完全不对。后来花了足足两天才发现是 S 盒写错了一位。SM4 的 S 盒是标准,不是建议。

ECB / CBC / CTR 模式

讲完基础结构,咱聊聊工作模式。SM4 本身只能处理 128 位分组,但实际数据哪有那么巧?数据长度可能不是 128 位的整数倍。这时候就需要模式来定规矩了。

ECB 模式:简单但不安全

ECB 模式最简单:把数据切成 128 位一块,每块独立加密。我刚开始学密码时觉得这模式挺好,但后来做项目发现它问题很大。重复的明文块会产生相同的密文块,这在图像、文件等有规律的数据里会暴露信息。

说实话,我建议你在生产环境里尽量别用 ECB。除非数据本身就已经随机化,比如加密硬编码的序列号。

CBC 模式:最常用的模式

CBC 模式引入了初始化向量(IV),让每个分组都与前一个加密结果异或后再加密。这样做的好处是:相同的明文块会输出不同的密文块。我大部分项目都用 CBC,因为它安全且容易理解。

但要注意,CBC 不能并行加密,因为每个分组依赖前一个。另外,IV 必须是随机的,且每次加密都要更换。

CTR 模式:可并行的性能担当

CTR 模式是计数器模式。它用一个不断递增的计数器,和密钥一起生成密钥流,再用这个流与明文异或。CTR 可以并行计算,性能拉满。

我在高频交易系统里用过 CTR 模式,多核 CPU 同时算,速度非常快。但你要自己管理计数器,不能重复使用相同的计数-密钥组合。

三种模式对比
模式 安全性 性能 是否可并行 适用场景
ECB 低 高 是 少数结构化数据
CBC 高 中 否 通用
CTR 高 高 是 高性能要求
SM4 算法核心流程结构图 SM4 算法核心流程 128 位明文 分组处理 Feistel 网络 (32 轮迭代) 轮函数 → S盒转换 → 线性变换 L → 轮密钥异或 密钥扩展:128 位密钥 → 32 个轮密钥 rk0 ~ rk31 ECB 模式 CBC 模式 CTR 模式 核心:Feistel 结构是骨架,密钥扩展是燃料,工作模式决定如何使用分组结果

我个人的建议是:如果你不确定用哪个模式,直接用 CBC 配合随机 IV。安全、稳定、不会出大问题。CTR 性能更好但需要更精心的设计。ECB 嘛,除了教科书很少会用到。