4. AES算法:AES的数学基础(有限域)、字节代换、行移位、列混合、轮密钥加

AES,高级加密标准。说白了,它就是现在对称加密的“扛把子”。你平时用的Wi-Fi、HTTPS、甚至手机里的文件加密,背后都有它的影子。

我第一次真正“看懂”AES,是在一次嵌入式设备的安全升级项目里。当时芯片算力有限,我得手撸一个轻量级实现。嗯,那会儿我才意识到,光会调库不行,得懂它骨子里的数学。

这一章,我们就把它拆开来看。别怕,数学部分我会用最直白的方式讲。

4.1 有限域:AES的“数学地基”

AES 的所有运算,都建立在一个叫 GF(2⁸) 的有限域上。你想想看,为什么是 2⁸?因为一个字节是 8 位,正好对应 0 到 255 这 256 个数。

在这个世界里,加减法就是简单的 异或(XOR)。乘除法稍微复杂点,但核心就两个东西:

  • 不可约多项式:AES 用的是 x⁸ + x⁴ + x³ + x + 1。你可以把它当成一个“模数”,乘法结果超过 8 位时,就用它来取余。
  • 乘法逆元:每个非零字节都有唯一的逆元。这个逆元,就是后面字节代换的核心。
我的小技巧: 刚开始学有限域乘法时,别硬算。记住几个常用值的乘法结果,比如 0x02 * 0x03 = 0x01,这在列混合里会反复出现。

4.2 字节代换(SubBytes):非线性的“保险柜”

字节代换,就是用一个固定的 S盒(S-box),把每个字节替换成另一个字节。这是 AES 里唯一的非线性操作,也是它抗攻击的关键。

S盒是怎么来的?两步走:

  1. 在 GF(2⁸) 里求乘法逆元。0 的逆元定义为 0。
  2. 对结果做一次仿射变换(一个固定的位运算+异或)。

为什么要加仿射变换?我举个例子。如果只用逆元,输入 0x01 会变成 0x01,输入 0x00 还是 0x00。这太危险了!仿射变换打乱了这种“不动点”,让攻击者没法轻易猜出规律。

避坑指南: 我曾经在项目中直接用了网上找的 S盒表,结果发现是错的。记住,S盒必须自己验证一遍:SubBytes(0x00) = 0x63,SubBytes(0x01) = 0x7c。这两个值对了,表基本就对了。

4.3 行移位(ShiftRows):让数据“流动”起来

行移位,名字很直白。AES 把 16 字节排成一个 4x4 的矩阵:

状态矩阵(State):
| a0  a4  a8  a12 |
| a1  a5  a9  a13 |
| a2  a6  a10 a14 |
| a3  a7  a11 a15 |

行移位规则:

  • 第 0 行:不动
  • 第 1 行:循环左移 1 个字节
  • 第 2 行:循环左移 2 个字节
  • 第 3 行:循环左移 3 个字节

结果就是,每一列的字节被“打散”到了不同列。这样,列混合才能让每个输出字节都依赖于所有输入字节。

注意: AES-128 和 AES-256 的行移位规则完全一样。但 AES-192 的移位量不同,第 2 行左移 2 字节,第 3 行左移 3 字节。别搞混了。

4.4 列混合(MixColumns):扩散的“搅拌机”

列混合,是 AES 里最像“数学”的一步。它把每一列看作一个 4 项多项式,然后乘以一个固定多项式 c(x) = 0x03·x³ + 0x01·x² + 0x01·x + 0x02,再模 x⁴ + 1。

说白了,就是下面这个矩阵乘法(在 GF(2⁸) 里):

| s'0 |   | 0x02 0x03 0x01 0x01 | | s0 |
| s'1 | = | 0x01 0x02 0x03 0x01 | | s1 |
| s'2 |   | 0x01 0x01 0x02 0x03 | | s2 |
| s'3 |   | 0x03 0x01 0x01 0x02 | | s3 |

每个新字节,都依赖于旧列里的所有 4 个字节。这就是“扩散”——改变一个输入字节,平均会改变一半的输出字节。

实现优化: 如果你在 32 位处理器上实现 AES,可以把列混合和行移位合并成“查表法”。用 4 个 256x32 位的 T 表,一次处理一列。速度能快好几倍。我在 ARM Cortex-M4 上试过,效果拔群。

4.5 轮密钥加(AddRoundKey):密钥的“注入”

轮密钥加,最简单的一步。就是把状态矩阵和轮密钥逐字节异或。

为什么简单?因为异或就是 GF(2⁸) 里的加法。而且它可逆——再异或一次同样的密钥,就还原了。

轮密钥从哪里来?从主密钥通过 密钥扩展 算法生成。AES-128 需要 10 轮,每轮 16 字节,加上初始的 16 字节,一共 176 字节。

密钥扩展的核心是:

  1. 每 4 个字节为一组(一个字)。
  2. 每轮开始的那个字,要先循环左移 1 字节,然后过 S盒,再异或上一个轮常数(RCON)。
  3. 其他字,直接异或前一个字和 4 个字前的字。
我曾经踩过的坑: 密钥扩展里的轮常数 RCON 是固定的:RCON[1] = 0x01,RCON[2] = 0x02,RCON[3] = 0x04…… 每次左移一位,超过 0x80 后要异或 0x1B(因为有限域)。我一开始忘了处理溢出,结果解密出来的全是乱码。

4.6 整体流程:一轮加密的“全景图”

下面这张图,把 AES 一轮加密的四个步骤串起来了。你可以看到,数据从左上角进来,经过 SubBytes、ShiftRows、MixColumns、AddRoundKey,最后变成密文输出。

AES 一轮加密流程图 AES 一轮加密流程 明文状态矩阵 1. SubBytes(字节代换) S盒替换,非线性变换 2. ShiftRows(行移位) 各行循环左移 3. MixColumns(列混合) 列内线性变换,扩散 4. AddRoundKey(轮密钥加) 与轮密钥异或 密文状态矩阵 注意 最后一轮没有 MixColumns AES-128 共 10 轮 AES-192 共 12 轮 AES-256 共 14 轮

解密过程就是加密的逆过程。顺序反过来:InvShiftRows、InvSubBytes、AddRoundKey(密钥顺序也反过来)、InvMixColumns。注意,解密时轮密钥加的顺序和加密时一样,只是密钥本身是逆序的。

4.7 小结:AES 为什么安全?

AES 的安全性,建立在三个设计原则上:

  • 混淆:SubBytes 的非线性变换,让密钥和密文的关系极其复杂。
  • 扩散:ShiftRows 和 MixColumns 让每个明文位的影响扩散到整个状态矩阵。
  • 密钥敏感性:轮密钥加确保每一轮都依赖不同的密钥位。

这三个原则,加上有限域这个坚实的数学基础,让 AES 至今没有被有效的攻击方法攻破。嗯,至少公开的没有。

我的建议: 如果你只是用 AES,直接用 OpenSSL 或你语言的标准库就行。但如果你想做硬件实现、侧信道防护或者定制化优化,那这一章的数学基础就是你的“内功”。别偷懒,动手算几个 S盒值,手写一遍列混合,你会对 AES 有完全不一样的理解。