高级加密标准(Advanced Encryption Standard, AES) 是美国联邦政府采用的分块 对称加密算法, 截至目前仍然不可攻破, 可被用于加密经NSA批准的加密模块中的绝密文档, 关于AES的官方文档可以参看FIPS 197. 该算法由比利时密码学家Daeman和Rijmen所设计, 结合这两位作者的名字, AES又称为Rijndael加密法.1
AES是一种分块加密算法, ECB模式需要严格将加密的信息按128比特分块,2 3而密钥长度可以分别选取为128比特, 192比特和256比特,4此时分别称AES-128, AES-192和AES-256, 穷举法破解它们的计算复杂度分别为2128, 2192和2256. 即使量子计算机成熟, 使用Grover算法也只能做到二次加速, 使它们的计算复杂度减半, 此时AES-256仍然安全, 但AES-128和AES-192不再安全.
AES算法运行在一个4×4 状态(state) 矩阵上, 每个矩阵的元素是1个字节, 每个字节包含8个比特, 也即
b0b1b2b3b4b5b6b7b8b9b10b11b12b13b14b15同理, AES-128的初始密钥矩阵表示为
k0k1k2k3k4k5k6k7k8k9k10k11k12k13k14k15AES加密算法需要用到上述代表明文的的状态矩阵和密钥的初始矩阵, 其加密步骤包括 字节替换(SubBytes), 行移位(RowShifts), 列混淆(MixColumns) 和 轮密钥加(AddRoundKey). 在AES-128中, 需要让明文经过10轮加密, 其中前9轮加密完整经历以上4个步骤, 最后1轮加密不经过列混淆, 而在AES-192和AES-256中, 加密轮数分别扩大为12轮和14轮, 最后1轮加密仍然不经过列混淆.
在介绍AES加密算法的具体步骤前, 我们先补充其有关的数学基础.
数学基础#
AES算法中的大部分操作都定义在Galois域GF(28)上, 每个状态矩阵中的字节{b7b6b5b4b3b2b1b0}由8个比特构成, 字节都可以表示为多项式
b(x)=b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0例如, {01100011}可以表示为多项式x6+x5+x+1.
为了在GF(28)上对两个元素进行加法运算, 表示这两个元素的的系数需要以2为模进行异或, 也即1⊕1=0, 1⊕0=1和0⊕0=0. 因此, 每个字节的加法运算是与其相关的8个比特的异或运算, 也即{a7a6a5a4a3a2a1a0}与{b7b6b5b4b3b2b1b0}的和为
{a7⊕b7a6⊕b6a5⊕b5a4⊕b4a3⊕b3a2⊕b2a1⊕b1a0⊕a0}例如, 以下三个运算是等价的
(x6+x4+x2+x+1)+(x7+x+1){01010111}⊕{10000011}{57}⊕{83}=x7+x6+x4+x2={11010100}={d4}(多项式)(二进制)(十六进制)其中, 由于多项式系数以2为模, 系数1等价于−1, 于是加法等价于减法. 例如, x4+x2表示相同的有限域元素x4−x2, −x4+x2和−x4−x2. 类似地, 任意元素与它自身的和为0元素.
另一方面, GF(28)上的乘法运算∙定义为多项式的乘积取模一个固定的8次不可约多项式
m(x)=x8+x4+x3+x+1如果令多项式b(x)和c(x)分别表示字节b和c, 那么b∙c表示
b(x)c(x)modm(x)例如, {57}∙{13}={fe}, 这是因为
(x6+x4+x2+x+1)(x4+x+1)=x10+x8+x7+x3+1并且
x10+x8+x7+x3+1modx8+x4+x3+x+1=x7+x6+x5+x4+x3+x2+x而取模后的多项式对应的二进制为{11111110}, 也即十六进制的{fe}. 此外, 如果c(x)=x, 也即c={02}, 那么乘积b∙{02}可以表示为XTIMES(b), 表示为
XTIMES(b)={{b6b5b4b3b2b1b00},{b6b5b4b3b2b1b00}⊕{00011011},b7=0b7=1于是
{57}∙{01}{57}∙{02}{57}∙{04}{57}∙{08}{57}∙{10}={57}=XTIMES({57})={ae}=XTIMES({ae})={47}=XTIMES({47})={8e}=XTIMES({8e})={07}从而{57}∙{13}={57}∙({01}⊕{02}⊕{10})={57}⊕{ae}⊕{07}={fe}.
在AES算法的列混淆步骤中, 需要用到矩阵乘法运算, 如果我们有一个输入矩阵 b0b1b2b3 , 使用以下固定的4×4矩阵左乘它可以得到输出矩阵 d0d1d2d3 , 也即
d0d1d2d3=a0a1a2a3a3a0a1a2a2a3a0a1a1a2a3a0b0b1b2b3其中
d0d1d2d3=(a0∙b0)⊕(a3∙b1)⊕(a2∙b2)⊕(a1∙b3)=(a1∙b0)⊕(a0∙b1)⊕(a3∙b2)⊕(a2∙b3)=(a2∙b0)⊕(a1∙b1)⊕(a0∙b2)⊕(a3∙b3)=(a3∙b0)⊕(a2∙b1)⊕(a1∙b2)⊕(a0∙b3)加密过程#
考虑如下明文: The pure-blooded
, 每一个ASCII字符为1字节, 共16字节, 字符下方为ASCII对应的16进制码.
T | h | e | | p | u | r | e | - | b | l | o | o | d | e | d |
---|
54 | 68 | 65 | 20 | 70 | 75 | 72 | 65 | 2d | 62 | 6c | 6f | 6f | 64 | 65 | 64 |
再给定以下16字节的密钥: Alice_Kunoji0930
, 下面将给出ECB模式下的AES-128加密明文的详细过程.
A | l | i | c | e | _ | K | u | o | n | j | i | 0 | 9 | 3 | 0 |
---|
41 | 6c | 69 | 63 | 65 | 5f | 4b | 75 | 6f | 6e | 6a | 69 | 30 | 39 | 33 | 30 |
First: 初始替代. 将明文和密钥按顺序存储在4×4矩阵中进行加法运算, 也即
54686520707572652d626c6f6f646564⊕416c6963655f4b756f6e6a6930393330=15040c43152a3910420c06065f5d5654(A.1)然后开始进行第一轮加密的字节替换, 这需要用到FIPS 197中的S盒将状态矩阵中的每一个元素进行替换, 例如元素5d
被替换为S盒中第5
行第d
列的元素4c
, 由此得到新的状态矩阵
59f2fe1a59e512ca2cfe6f6fcf4cB120(A.2)Second: 行移位. 让状态矩阵(A.2)
的第i行中的每个字节向左移动i−1位, 这里i=1,2,3,4. 于是新的状态矩阵又变为
59e56f2059feb11a2c4cfecacff2126f(A.3)Third: 列混淆. 将状态矩阵(A.3)
左乘 以一个固定的4×4矩阵
0201010303020101010302010101030259e56f2059feb11a2c4cfecacff2126f=c9190221006cf090b867c249f569a874(A.4)根据乘法原理可知元素d0,0的计算方式为
d0,0=({02}∙{59})⊕({03}∙{e5})⊕{6f}⊕{20}={b2}⊕{34}⊕{6f}⊕{20}={c9}Fourth: 轮密钥加. 现在需要使用一个新的子密钥矩阵与状态矩阵(A.4)
进行异或操作, 得到子密钥矩阵的方法是进行 密钥扩张(KeyExpansion).5在AES-128中, 我们需要扩张10轮密钥, 总共得到176字节的密钥(含16字节的初始密钥).6
记初始密钥矩阵为的第1−4列分别为w[0], w[1], w[2]以及w[3], 其中
w[0]=416c6963,w[1]=655f4b75,w[2]=6f6e6a69,w[3]=30393330再设l为轮数, 则子密钥矩阵由w[4l], w[4l+1], w[4l+2]以及w[4l+3]构成, 并且
w[j]={w[j−4]⊕w[j−1],w[j−4]⊕g(w[j−1]),jmod4=0jmod4=0这里的函数g使得w[j−1]经过字循环、字节替换和轮常量异或:
现在我们需要得到第1轮的子密钥矩阵分块w[4], w[5], w[6]以及w[7]. 首先计算得到
g(w[3])=13c30404⟹w[4]=w[0]⊕g(w[3])=52af6d67于是
w[5]=37f02612,w[6]=589e4c7b,w[7]=68a77f4b然后进行轮密钥加, 也即让子密钥矩阵与状态矩阵(A.4)
进行异或, 得到首轮加密后的状态矩阵
c9190221006cf090b867c249f569a874⊕52af6d6737f02612589e4c7b68a77f4b=9bb66f46379cd682e0f98e329dced73f(A.5)第一轮加密结束, 随后的第二轮至第九轮加密过程完全相同, 只是轮常量随轮数发生变化, 最后一轮除了不进行列混淆外, 其余步骤也与之前相同. 最后, 我们将十轮加密得到的状态矩阵展开如下, 最后一轮得到的Round 10
即为AES-128最终得到的密文.
Round 0 | 54 | 68 | 65 | 20 | 70 | 75 | 72 | 65 | 2d | 62 | 6c | 6f | 6f | 64 | 65 | 64 |
---|
Round 1 | 9b | b6 | 6f | 46 | 37 | 9c | d6 | 82 | e0 | f9 | 8e | 32 | 9d | ce | d7 | 3f |
Round 2 | 31 | 90 | b9 | 33 | f0 | 76 | 09 | a6 | 87 | 0f | a0 | 76 | b0 | 54 | 49 | 1c |
Round 3 | 24 | 72 | 26 | a0 | df | 01 | 97 | 66 | e1 | 75 | 06 | 75 | 9a | 54 | f7 | 51 |
Round 4 | 89 | 4c | 01 | 0f | 72 | ea | 50 | 51 | f3 | 78 | 31 | 16 | d4 | 9f | 3c | fc |
Round 5 | 29 | 3d | 37 | 34 | 3a | 13 | 5a | 40 | 85 | 64 | fe | 86 | d1 | 80 | c5 | 65 |
Round 6 | ad | b6 | 10 | 64 | 15 | fd | 3f | b9 | db | 29 | 55 | 9f | f0 | 46 | 42 | 62 |
Round 7 | 34 | 76 | 72 | 4f | f3 | d3 | 57 | 1f | f0 | 71 | 9b | 6f | 8b | 90 | a3 | ab |
Round 8 | df | 20 | 72 | 2d | 83 | 8c | 99 | e5 | 21 | 65 | 8e | ff | 37 | e5 | 14 | 16 |
Round 9 | a8 | 8b | 35 | bb | a2 | 4c | bd | 8a | 9e | 93 | ed | 5e | 75 | e3 | ad | cb |
Round 10 | 4a | 67 | 4a | 3e | 26 | 65 | 0a | 72 | 81 | 76 | 30 | 94 | 77 | 69 | a1 | b9 |
解密过程#
解密过程是加密的逆运算, 流程与加密完全相反, 具体是按顺序执行: 轮密钥加、逆列混淆、逆行移位、逆字节替换.
在第1轮解密中, 首先执行 轮密钥加, 即用第10轮的子密钥矩阵与密文进行异或, 得到
4a674a3e26650a72817630947769a1b9⊕884e1f211cb99f988a67a6eaea54dbe1=c229551f3adc95ea0b11967e9d3d7a58(A.6)接下来进行逆行移位, 让状态矩阵(A.6)
的第i行中的每个字节向右移动i−1位, 这里i=1,2,3,4, 于是状态矩阵变为
c23d96ea3a297a7e0bdc55589d11951f(A.7)然后使用FIPS 197中的逆S盒进行逆字节替换得到Round 9
a88b35bba24cbd8a9e93ed5e75e3adcb(A.8)注意, 由于是第1轮解密, 所以没有用到逆列混淆操作. 现在开始第2轮解密, 使用第9轮的子密钥矩阵与(A.8)
异或得到
a88b35bba24cbd8a9e93ed5e75e3adcb⊕7db134f194f780b996de397260337d0b=d53a014a36bb3d33084dd42c15d0d0c0(A.9)然后执行逆列混淆, 将状态矩阵(A.9)
左乘一个固定的矩阵得到
0e090d0b0b0e090d0d0b0e09090d0b0ed53a014a36bb3d33084dd42c15d0d0c0=9e641947cc4dfad8fdd940d99ab7ee16(A.10)对状态矩阵(A.10)
进行逆行移位和逆字节替换即可得到Round 8
的状态矩阵(A.11)
9e641947cc4dfad8fdd940d99ab7ee16⟹9eb740d8ec64eed9fd4d19169ad9fa47⟹df20722d838c99e521658eff37e51416(A.11)之后的第3−10轮的解密流程与此相同, 不再赘述.
AES-192和AES-256#
一旦了解清楚AES-128的加密和解密, 我们就能轻易拓展到AES-192和AES-256, 它们的主体几乎一样, 最大的不同在于密钥扩张.
在AES-192中, 加密轮数变为12, 初始密钥矩阵变为4×6矩阵, 每列为w[j], j=0,1,2,3,4,5, 共包含24字节.
k0k1k2k3k4k5k6k7k8k9k10k11k12k13k14k15k16k17k18k19k20k21k22k23在初始替换中, 与AES-128一样使用w[0], w[1], w[2]以及w[3]与明文矩阵异或, 后续的行移位、字节替换和列混淆与AES-128相同. 在轮密钥加中, 仍然使用w[4l], w[4l+1], w[4l+2]以及w[4l+3]构成的子密钥与列混淆后的状态矩阵异或, 但是生成子密钥的密钥扩张过程不同:
如果j是6的倍数. 则w[j]=w[j−6]⊕g(w[j−1]); 如果j不是6的倍数, 则w[j]=w[j−6]⊕w[j−1]. 其中, g函数表示将w[j−1]按先后顺序进行字循环、字节替换以及与Rcon[j/6]异或.
由于AES-192最大将初始密钥扩张至w[51], 因此密钥扩张只需用到8个轮常量.
在AES-256中, 加密轮数变为14, 初始密钥矩阵变为4×8矩阵, 每列为w[j], j=0,1,2,3,4,5,6,7, 共包含32字节.
k0k1k2k3k4k5k6k7k8k9k10k11k12k13k14k15k16k17k18k19k20k21k22k23k24k25k26k27k28k29k30k31初始替换步骤与AES-192相同, 仍是选取前4列w[j]与初始状态矩阵异或, 并在后续的轮密钥加过程中使用扩张得到的子密钥矩阵与状态矩阵异或. 然而, AES-256的密钥扩张又与AES-128和AES-192不同:
如果j是8的倍数, 则w[j]=w[j−8]⊕g(w[j−1]); 如果j+4是8的倍数, 那么w[j]=w[j−8]⊕s(w[j−1]); 如果不是以上两种情况, 则w[j]=w[j−8]⊕w[j−1]. 其中, s函数表示将w[j−1]进行字节替换, g函数表示将w[j−1]按先后顺序进行字循环、字节替换以及与Rcon[j/8]异或.
由于AES-256密钥扩张至w[59], 故而只需用到7个轮常量.