AES算法简述
分组加密有几种方式分别是:
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。将整个明文分成若干段相同的小段,然后对每一小段进行加密
00112233445566778899aabbccddee 还差2位hex(1个字节),填充01:00112233445566778899aabbccddee01
00112233445566778899aabbccddeeff 等于128比特,需要填充一整个分组:00112233445566778899aabbccddeeff(hex)==> 00112233445566778899aabbccddeeff10101010101010101010101010101010
最少填充一字节,最多填充一整个分组,不能不填充。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密
CFB/OFB实际上是一种反馈模式,目的也是增强破解的难度。密码算法的输出(指密码key而不是密文)会反馈到密码算法的输入中,OFB模式并不是通过密码算法对明文直接加密,而是通过将明文分组和密码算法的输出进行XOR来产生密文分组。
ECB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。
AES-128接收16字节的明文输入,16字节的密钥,输出16字节的密文结果。
入参:
明文:128比特(32个十六进制数、16个字节)
KEY:128比特(32十六进制数、16个字节)
出参:128比特(32个十六进制数、16个字节)
明文:7a68656e677368616f6b756e79796473(hex)
密钥:0123456789abcdef0123456789abcdef(hex)
下方是整体流程图:
首先看整体的流程图,我们发现,AES的整体图景可以分成左右两块,即明文的处理和密钥的编排。明文的处理主体是一个初始化轮密钥加和十轮运算,在初始化轮密钥加十轮运算中都需要使用密钥编排的结果。密钥编排将16个字节经过运算推演出11组轮密钥,每一组16个字节,称之为K0,K1,K2…K10
密钥编排
下面我们看一下密钥扩展是如何算出来的,这是我们的密钥Key 0123456789abcdef0123456789abcdef。为了区分密钥和密钥编排后的轮密钥,我们将此时的密钥叫主密钥。
在AES-128中,密钥扩展后得16*11共176字节,使用时逐十六个字节划分成K0,K1,…K10使用,但是在生成时,它是逐四个字节生成的,即44*4。我们不妨用数组来描述它,即一个包含了44个元素的数组,叫W。
这四十四个元素的生成规则有三种,如下图所示:
不同颜色代表了不同规则。最上方的W0,W1,W2,W3 就是主密钥本身切成四段。
Key = 0123456789abcdef0123456789abcdef
W0 = 01234567
W1 = 89abcdef
W2 = 01234567
W3 = 89abcdef
左侧的红色部分,W4,W8,W12,….W40的生成复杂一点。
Wn=g(Wn−1) xor Wn−4
xor 是异或运算,比如 W4=g(W3) xor W0。g(当前元素前面那个元素) 异或 当前元素头顶上那个元素。
那么关键点就是这个 g 函数了, g 函数一共三个步骤——循环左移、S盒替换、字节异或。 我们以W4运算中所需的W3为例。
W3=89abcdef
g函数——循环左移、S盒替换、字节异或
循环左移
首先是循环左移,规则固定—— 将最左边的一个字节挪到右边即可
循环左移后为 abcdef89
S盒替换
第二步是S盒替换,S盒替换听着很高级,但操作上很简单——将数值本身作为索引取出S数组中对用的值。S盒是固定的。
1 | SBox = [ |
S盒的背后有十分复杂的知识,但好在我们并不需要去了解。
AB CD EF 89 经过S盒替换后为 62 BD DF A7
最高字节和一个固定常量异或
最后一个步骤更简单,将上一步结果中的最高字节和一个固定常量异或。W4的生成是第一个,用如下rcon表的第一个元素0x1。W40即第十次,用最后一个元素0x36.
1 | rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36] |
62 BD DF A7 最高位62与rcon第一个元素,即0x01异或后为:63 BD DF A7
W4 = g(W3) xor W0
= g(0x89abcdef) xor 0x01234567
= 0x63BDDFA7 xor 0x01234567
= 0x629e9ac0
最后一步可以直接用python算:
1 | print(hex(0x63BDDFA7 ^ 0x01234567)) |
上图中蓝色和红色的部分都讲完了,那么橙色部分呢?相当的简单,和红色部分类似,去掉g函数即可
Wn=Wn−1 xor Wn−4
打个比方,W5 = W4 ^ W1 = 0x629e9ac0 ^ 0x89abcdef = 0xeb35572f
如下是完整的密钥编排部分的Python代码
1 | Sbox = ( |
运行结果如下
1 | K00:0123456789abcdef0123456789abcdef |
我们对AES密钥编排部分的学习就基本完成了。
明文运算
现在开始学习明文的运算,即图中左边的部分。首先,我们要调整明文的格式,在AES中,数据以state的形式计算、中间存储和传输,中文名即状态。
从明文转到state形式很简单,以我们的明文7a68656e677368616f6b756e79796473为例。从上到下,从左到右。千万不要颠倒顺序,第一行不是“7a 68 65 6e”。除此之外,state中的数,我们一般用十六进制表示,且不加0x前缀,这样看着比较舒服。除非特意强调是十进制,否则下文均为十六进制。
接下来是轮密钥加步骤,因为是第一次轮密钥加步骤,所以也叫初始轮密钥加。轮密钥加步骤听着很怪,但实质很简单。只需要将对应的轮密钥和
初始的轮密钥加使用 K0 ,0123456789abcdef0123456789abcdef。
接下来就是十轮主运算,看如下的伪代码,我们可以清楚看到一轮运算中有什么,以及第十轮和前九轮有什么区别。
初始的明文转
前九轮运算中,包含四个步骤: 字节替换,循环左移,列混淆,轮密钥加。
第十轮中,包含三个步骤:字节替换,循环左移,轮密钥加。相比前九轮缺一个列混淆,其余相同。
十轮运算中的轮密钥加,和初始轮密钥加相比,除了使用的轮密钥不同外,并无不同,分别为K1…..K10。
而
其循环左移规则如下:第一行不循环左移,第二行循环左移1字节,第三行循环左移2字节,第四行循环左移3字节。
相对复杂的是列混淆步骤,列混淆步骤涉及两块知识,1是矩阵乘法,2是伽罗瓦域的加法和乘法。前者还好,后者属于抽象代数的内容,比较复杂。
先看第一块——矩阵乘法。首先演示简单的矩阵相乘,
左边准备相乘的两个矩阵,我们称它俩为矩阵A和矩阵B,如何求结果矩阵中的abcd ?规则如下:第m行第n列的值等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。
a是第一行第一列,那么就是A的第一行和B的第一列元素乘积之和
同理可得
即
所谓乘积之和,指乘法和加法。
再来看AES列混淆中的矩阵乘法,我们的
看起来有些复杂,小例子中是2*2的矩阵要算4个值,这里是4*4的矩阵要算16个值。我们这里只管第一列,其他列的计算类似。
列混淆中的的加法和乘法并不是小例子或日常中的那样,其中的加法指 异或运算 。2A + 3B + C + D 即 2A ^ 3B ^ C ^ D,这也是
x * 1,结果为x本身
1 | def mul_by_01(num): |
x * 2,首先切换到二进制形式,最高位为0时,比特串左移1比特,最右边补0即可。如果最高位为1,比特串左移1比特,最右边补0,最后再异或上 0x1B
1 | def mul_by_02(num): |
x * 3 = (x * 02) + x,注意”加“是异或哦。
1 | def mul_by_03(num): |
列混淆,就这么讲完了。下面看完整的AES代码实现
1 | Sbox = ( |