3. AES算法原理(上):整体结构、字节代换、行移位
好,咱们正式开始啃 AES 的内核。 先给你打个底:AES 是一种 分组密码,每次处理 16 个字节(128 位)。 不管密钥是 128、192 还是 256 位,数据那块始终是 4×4 的字节矩阵。 我当年刚接触时总把密钥长度和分组大小搞混,后来在纸上画了十几遍才理顺——**分组固定,密钥可变**。
3.1 整体结构:一个轮函数的四次变换
AES 的加密过程可以理解成「重复执行一个标准轮函数」,最后再加一次特殊的 Final Round。 标准轮函数由四个子操作组成:
- SubBytes(字节代换)
- ShiftRows(行移位)
- MixColumns(列混合)
- AddRoundKey(轮密钥加)
Final Round 会去掉 MixColumns,只做 SubBytes → ShiftRows → AddRoundKey。 为什么?因为做混合那一步会给解密带来不必要的麻烦,这点咱们后面讲解密时再细说。
AES-128 → 10 轮(包含 Final Round)
AES-192 → 12 轮
AES-256 → 14 轮
我个人习惯把 AES 的轮函数想象成一条「加工流水线」:每个字节先被 S‑box 替换,再按行打乱,然后混合列,最后跟本轮子密钥异或。 前两步(SubBytes 和 ShiftRows)负责「混淆 + 扩散」,是抵抗线性分析和差分分析的主力。下面咱们拆开看。
3.2 字节代换(SubBytes)—— 用 S‑box 给每个字节换脸
SubBytes 是 AES 里唯一的非线性操作。 算法把 State 矩阵里每个字节当作索引,去一个固定的 16×16 查找表(S‑box)里取出新值替换掉原来的字节。 说白了就是:每个字节都整容,且整容方案和它自身的位置无关,只跟它当前值有关。
用 C 写 SubBytes 的伪代码大概是这样的:
void SubBytes(uint8_t state[4][4], const uint8_t sbox[256]) {
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
state[row][col] = sbox[state[row][col]];
}
}
}
你在实现时可能会想:为什么不用编号叫 row/col 而是用 row 对应行? 嗯,这里要注意:AES 的 State 是用列优先排布存储的,但 SubBytes 对每个字节独立操作,所以行列顺序影响不大。 真正让你头疼的是后面的 ShiftRows 和 MixColumns,它们依赖矩阵坐标。
3.3 行移位(ShiftRows)—— 把行内字节左转转
ShiftRows 做的事非常简单: 把 State 矩阵的每一行,分别向左循环移位不同的长度。
| 行号 | 移位字节数 |
|---|---|
| 第 0 行 | 0 |
| 第 1 行 | 1 |
| 第 2 行 | 2 |
| 第 3 行 | 3 |
第 0 行不动,第 1 行循环左移 1 个字节,第 2 行移 2 个,第 3 行移 3 个。 这样做的目的是把同一列里的字节散布到不同的列里。 你想,如果只有 SubBytes,攻击者很容易把统计规律限制在列内;ShiftRows 一搅,列与列之间的关联就断了。
ShiftRows 的代码实现也简单:
void ShiftRows(uint8_t state[4][4]) {
// row 1
uint8_t temp = state[1][0];
state[1][0] = state[1][1];
state[1][1] = state[1][2];
state[1][2] = state[1][3];
state[1][3] = temp;
// row 2: 左移 2 = 右移 2,但实现时我习惯直接交换两次
// 用循环或直接展开均可
// ... 略,类似处理
}
实际项目中我更喜欢用查表法做 ShiftRows:预先算好字节在新矩阵中的索引,然后复制。 尤其是搞 AES-NI 指令集之前的老 CPU,查表比循环移位快很多。
3.4 第一阶段总结——为什么这两步要放在一起看?
SubBytes 提供了混淆:让字节与密钥的关系变得高度复杂。 ShiftRows 提供了扩散:让一个字节的变动能影响到后续多列。 两者合起来,再配合 MixColumns,就是 AES 抵抗线性密码分析和差分密码分析的底气所在。 我个人在做选型对比(AES vs SM4)时,经常会把 S‑box 的代数性质和移位模式拿出来做基准评估——这直接决定了算法的安全冗余和实现效率。
· SubBytes:每个字节独立非线性变换,用 S‑box 表。
· ShiftRows:按行循环移位,打散列关联。
· 两者都是可逆的,解密时做逆操作即可。
好了,前半段就聊到这儿。下面一章咱们接着看 MixColumns 和 AddRoundKey—— 这两步才是真正把密钥揉进数据里的环节,也是 AES 性能的瓶颈所在。