AES 背景
AES 全称 Advanced Encryption Standard(高级加密标准)。它的出现主要是为了取代 DES 加密算法的,因为 DES 算法的密钥长度是 56 位,因此算法的理论安全强度是 2^56。但二十世纪中后期正是计算机飞速发展的阶段,元器件制造工艺的进步使得计算机的处理能力越来越强,所以还是不能满足人们对安全性的要求。于是 1997 年 1 月 2 号,美国国家标准技术研究所宣布希望征集高级加密标准,用以取代 DES。AES 也得到了全世界很多密码工作者的响应,先后有很多人提交了自己设计的算法。最终有5个候选算法进入最后一轮:Rijndael,Serpent,Twofish,RC6 和 MARS。最终经过安全性分析、软硬件性能评估等严格的步骤,Rijndael 算法获胜。
AES 密码与分组密码 Rijndael 基本上完全一致,Rijndael 分组大小和密钥大小都可以为 128 位、192 位和 256 位。然而 AES 只要求分组大小为 128 位,因此只有分组长度为 128 位的 Rijndael 才称为 AES 算法。本文只对分组大小 128 位,密钥长度也为 128 位的 Rijndael 算法进行分析。密钥长度为 192 位和 256 位的处理方式和 128 位的处理方式类似,只不过密钥长度每增加 64 位,算法的循环次数就增加 2 轮,128 位循环 10 轮、192 位循环 12 轮、256 位循环 14 轮。
AES 功能
给定一个 128 位的明文和一个 128 位的密钥,输出一个 128 位的密文。这个密文可以用相同的密钥解密。虽然 AES 一次只能加密 16 个字节,但我们只需要把明文划分成每 16 个字节一组的块,就可以实现任意长度明文的加密。如果明文长度不是 16 个字节的倍数,则需要填充,目前填充方式主要是 PKCS7 / PKCS5。
AES 加密原理
下来主要分析 16 个字节的加解密过程,下图是 AES 算法框架。
密钥生成
密钥生成流程
- 将初始 128 位密钥,按每 8 位一个字节组成 16 个字节,得到 k1、k2、……、k16;
- 把 16 个字节按顺序从上到下、从左到右进行排列,得到 4 * 4 的矩阵;
- 把矩阵每一列合成一个 32 位的值,得到 w[0]、w[1]、w[2]、w[3];
- 依次求解 w[j], j 属于 [4, 43] 中的整数,若 j%4=0,则 w[j]=w[j-4] ⊕ g(w[j-1]),否则 w[j]=w[j-4] ⊕ w[j-1];
- 在生成 w 数组中,每 4 个为一个子密钥一共 11 个,分别是 子密钥k0、子密钥k1、…… 、子密钥k10。
G 函数
- 将 w 循环左移 1 个字节(w 是一个 4 个字节的数组);
- 分别对每个字节做 S 盒置换(字节代换层的 S 盒,下文会介绍);
- 将 w 的第一个字节与轮常量 RC 进行异或运算。
RC = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}
关于轮常量的生成下文会介绍。
主要作用:一是增加密钥编排中的非线性;二是消除AES中的对称性。这两种属性都是抵抗某些分组密码攻击必要的。
AES 加密流程
- 把 128 位的明文,按照每 8 位一个字节,组成 16 个字节;
- 把 16 个字节按顺序从上到下、从左到右进行排列,得到 4 * 4 的明文矩阵;
- 把 4 * 4 的明文矩阵和第一个 4 * 4 的子密钥矩阵进行异或运算,得到新的矩阵 P(密钥加法层);
- 对矩阵 P 进行 S 盒置换得到矩阵 P1,具体规则:S 盒是一个 16 * 16 的矩阵,依次遍历 P 中元素,元素高四位值为行号,低四位值为列号,然后在 S 盒中取出对应的值。(字节代换层);
- 对 4 * 4 的矩阵 P1 进行行位移得到矩阵 P2,具体规则:第一行不变,第二行向左移 1 位,第三行向左移 2 位,第四行向左移 3 位(Shift Rows 层);
- 把矩阵 P2 和一个固定的 4 * 4 的矩阵 GF,进行伽罗瓦域的乘法得到矩阵 P3(Mix Column 层,下文会介绍伽罗瓦域的乘法);
- 把矩阵 P3 和当前轮次的密钥矩阵进行异或运算得到矩阵 P4(密钥加法层);
- 重复步骤 4、5、6、7,注意最后一轮不需要 步骤 6 ,最后输出密钥。
接下来详细解释一下几个关键步骤。
密钥加法层
明文矩阵和当前回次的子密钥矩阵进行异或运算。
字节代换层
字节代换层的主要功能是通过 S 盒完成一个字节到另外一个字节的映射。
依次遍历 4 * 4 的明文矩阵 P 中元素,元素高四位值为行号,低四位值为列号,然后在 S 盒中取出对应的值。
tmp = P[i][j];
P'[i][j] = S[tmp & 0xf0][tmp & 0x0f]
Shift Rows 层(行位移)
行位移操作最为简单,它是用来将输入数据作为一个 4 * 4 的字节矩阵进行处理的,然后将这个矩阵的字节进行位置上的置换。ShiftRows 子层属于 AES 手动的扩散层,目的是将单个位上的变换扩散到影响整个状态当,从而达到雪崩效应。它之所以称作行位移,是因为它只在 4 * 4 矩阵的行间进行操作,每行 4 字节的数据。在加密时,保持矩阵的第一行不变,第二行向左移动 1 个字节、第三行向左移动 2 个字节、第四行向左移动 3 个字节。
Mix Column 层(列混淆)
列混淆层是 AES 算法中最为复杂的部分,属于扩散层,列混淆操作是 AES 算法中主要的扩散元素,它混淆了输入矩阵的每一列,使输入的每个字节都会影响到 4 个输出字节。行位移层和列混淆层的组合使得经过三轮处理以后,矩阵的每个字节都依赖于 16 个明文字节成可能。其实质是在有限域 GF(2^8) 上的多项式乘法运算,也称伽罗瓦域上的乘法。
伽罗瓦域
伽罗瓦域上的乘法在包括加/解密编码和存储编码中经常使用,AES 算法就使用了伽罗瓦域 GF(2^8) 中的运算。以 2^n 形式的伽罗瓦域来说,加减法都是异或运算,乘法相对较复杂一些,下面介绍 GF(2^n) 上有限域的乘法运算。
本原多项式: 域中不可约多项式,是不能够进行因子分解的多项式,本原多项式是一种特殊的不可约多项式。当一个域上的本原多项式确定了,这个域上的运算也就确定了,本原多项式一般通过查表可得,同一个域往往有多个本原多项式。通过将域中的元素化为多项式的形式,可以将域上的乘法运算转化为普通的多项式乘法模以本原多项式的计算。比如 g(x) = x^3+x+1 是 GF(2^3) 上的本原多项式,那么 GF(2^3) 域上的元素 3*7 可以转化为多项式乘法:
3*7(in GF(2^3)) → (x+1)*(x^2+x+1) mod g(x) = x^3+1 mod x^3+x+1 = x → 3*7 = 2
乘二运算: 无论是普通计算还是伽罗瓦域上运算,乘二计算是一种非常特殊的运算。普通计算在计算机上通过向高位的移位计算即可实现,伽罗瓦域上乘二也不复杂,一次移位和一次异或即可。从多项式的角度来看,伽罗瓦域上乘二对应的是一个多项式乘以 x,如果这个多项式最高指数没有超过本原多项式最高指数,那么相当于一次普通计算的乘二计算,如果结果最高指数等于本原多项式最高指数,那么需要将除去本原多项式最高项的其他项和结果进行异或。
比如:GF(2^8)(g(x) = x^8 + x^4 + x^3 + x^2 + 1)上 15*15 = 85 计算过程。
15 写成生成元指数和异或的形式 2^3 + 2^2 + 2^1 + 1,那么:
15*15 = 15*(2^3 + 2^2 + 2^1 + 1) = 2*(2*(2*15 + 15) + 15) + 15 = 85
乘二运算计算过程:
有限域内数 | 操作 | 二进制值 | 十进制值 |
---|---|---|---|
0 | 0 0 0 0 0 0 0 0 | 0 | |
15 | 0 0 0 0 1 1 1 1 | 15 | |
2*15 | 移位 | 0 0 0 1 1 1 1 0 | 30 |
2*15+15 | 异或 | 0 0 0 1 0 0 0 1 | 17 |
2(215+15) | 移位 | 0 0 1 0 0 0 1 0 | 34 |
2(215+15)+15 | 异或 | 0 0 1 0 1 1 0 1 | 45 |
2(2(2*15+15)+15) | 移位 | 0 1 0 1 1 0 1 0 | 90 |
2(2(2*15+15)+15)+15 | 异或 | 0 1 0 1 0 1 0 1 | 85 |
列混淆:就是把两个矩阵的相乘,里面的运算,加法对应异或运算,乘法对应伽罗瓦域 GF(2^8) 上的乘法(本原多项式为:x^8 + x^4 + x^3 + x^1 + 1)。
unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};
unsigned char PlainArray[4][4] =
{
p1, p5, p9, p13,
p2, p6, p10, p14,
p3, p7, p11, p15,
p4, p8, p12, p16
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
result[i][j] =
Galois(MixArray[i][0], PlainArray[0][j]) ^
Galois(MixArray[i][1], PlainArray[1][j]) ^
Galois(MixArray[i][2], PlainArray[2][j]) ^
Galois(MixArray[i][3], PlainArray[3][j]);
}
}
Galois
函数为伽罗瓦域上的乘法。
AES 解码原理
解码过程和 DES 解码类似,也是一个逆过程。基本的数学原理也是:一个数进行两次异或运算就能恢复,S ^ e ^ e = S。
密钥加法层
通过异或的特性,再次异或就能恢复原数。
逆Shift Rows层
恢复Shift Rows层的移动。
逆Mix Column层
通过乘上正矩阵的逆矩阵进行矩阵恢复。
一个矩阵先乘上一个正矩阵,然后再乘上他的逆矩阵,相当于没有操作。
逆字节代换层
通过再次代换恢复字节代换层的代换操作。
比如:0x00 字节的置换过程
- 加密:0x00 通过 S 盒置换得到 0x63;
- 解密:0x63 通过逆 S 盒置换得到 0x00。
关于轮常量 RC 的计算
轮常量生成规则如下:
RC[0] = 1;
RC[i] = 2 * RC[i - 1] //在域 GF(2^8) 上的乘法,本原多项式:x^8 + x^4 + x^3 + x^1 + 1
RC[8] = 2 * RC[7] = 2 * 0x80 = x * x^7 mod x^8 + x^4 + x^3 + x^1 + 1
= x^4 + x^3 + x^1 + 1 = 11011 = 27
= 0x1B
RC[9] = 2 * RC[8] = 2 * 0x1B = x * (x^4 + x^3 + x^1 + 1) mod x^8 + x^4 + x^3 + x^1 + 1
= x^5 + x^4 + x^2 + x = 110110 = 54
= 0x36
关于 AES192 和 AES256
算法原理和 AES128 一样,只是每次加解密的数据和密钥大小为 192 位和 256 位。加解密过程几乎是一样的,只是循环轮数增加,所以子密钥个数也要增加,最后轮常量 RC 长度增加。
加密名称 | 密钥和分组大小 | 轮次 | 子密钥个数 |
---|---|---|---|
AES128 | 128位 | 10 | 11 |
AES192 | 192位 | 12 | 13 |
AES256 | 256位 | 14 | 15 |
参考资料
- [原创]密码学基础:AES加密算法
- 密码算法详解——AES
- AES标准及Rijndael算法解析
- 伽罗瓦域上的乘法
- 基于原理逐步实现AES加密算法