AES加密算法即密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法(2000年10月2日,比利时密码专家Joan Daemen和Vincent Rijmen提出的Rijindael),是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
①抵抗所有已知的攻击。
②在多个平台上速度快,编码紧凑。
③设计简单。
AES是一个迭代型分组密码,把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。
AES的处理单位是字节,密钥长度可变,在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同。
AES的加密公式为C = E(K,P),在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同。也就是说,一个明文分组会被加密10轮。AES的核心就是实现一轮中的所有操作。
AES的处理单位是字节,128位的输入明文分组P和输入密钥K都被分成16个字节,分别记为P = P0 P1 … P15 和 K = K0 K1 … K15。如,明文分组为P = abcdefghijklmnop,其中的字符a对应P0,p对应P15。一般地,明文分组用字节为单位的正方形矩阵描述,称为状态矩阵。在算法的每一轮中,状态矩阵的内容不断发生变化,最后的结果作为密文输出。该矩阵中字节的排列顺序为从上到下、从左至右依次排列(a00,a10,a20,a30,a01,a11,a21,a31 …的顺序),如下图所示:
现在假设明文分组P为"abcdefghijklmnop",则对应上面生成的状态矩阵图如下:
类似地,128位密钥也是用字节为单位的矩阵表示,矩阵的每一列被称为1个32位比特字,在矩阵中字是案列排序。通过密钥编排函数该密钥矩阵被扩展成一个44个字组成的序列W[0],W[1], … ,W[43],该序列的前4个元素W[0],W[1],W[2],W[3]是原始密钥,用于加密运算中的初始密钥加;后面40个字分为10组,每组4个字(128比特)分别用于10轮加密运算中的轮密钥加,如下图所示:
上图中,设K = “abcdefghijklmnop”,则K0 = a, K15 = p, W[0] = K0 K1 K2 K3 = “abcd”。
Rijndael 的轮函数由 4 个不同的计算部件组成,分别是:
AES未使用Feistel结构。AES其前9轮由4个不同的变换组成:字节代替、行移位、列混淆和轮密钥加。最后一轮仅包含三个变换。而在第一轮前面有一个起始的单变换(轮密钥加),可以视为0轮(明文分组P与初始密钥key做异或运算)
AES解密过程仍为10轮,每一轮的操作是加密操作的逆操作。由于AES的4个轮操作都是可逆的,因此,解密操作的一轮就是顺序执行逆行移位、逆字节代换、轮密钥加和逆列混合。同加密操作类似,最后一轮不执行逆列混合,在第1轮解密之前,要执行1次密钥加操作。完整结构如下图:
SubBytes()变换是一个基于S盒的非线性置换,它用于将每一个字节通过一个简单的查表操作映射为另一个字节
映射方法:把输入字节的高4位作为S盒的行值,低4位作为列值,然后取出S盒中对应行和列交叉位的元素作为输出;而字节代换逆操作也就是查逆 S 盒来变换。
S盒和逆S盒都是一个16×16个字节的矩阵,包含了8位所能表示的256个数的一个置换和逆置换。
例如,十六进制数{95}对应S盒的9行5列,在S盒中的值为{2A},所以{95}就被代替为{2A}。
逆S盒与其类似,逆推。S盒如下图:
行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节,4、6、8个字如下图所示:
列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵,如下图的公式所示:
状态矩阵中的第j列(0 ≤j≤3)的列混合可以表示如下所示:
轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,如下图所示。其中,密钥Ki中每个字W[4i],W[4i+1],W[4i+2],W[4i+3]为32位比特字,包含4个字节;轮密钥加过程可以看成是字逐位异或的结果,也可以看成字节级别或者位级别的操作。也就是说,可以看成S0 S1 S2 S3 组成的32位字与W[4i]的异或运算。
轮密钥由种子密钥通过密钥编排算法得到,轮密钥长度等于分组长度 Nb 。
密钥编排指从种子密钥得到轮密钥的过程,它由密钥扩展和轮密钥选取两部分组成。
基本原则如下:
1)轮密钥的比特数等于分组长度乘以轮数加1;
例如要将 128 比特的明文经过 10 轮的加密,则总共需要 比特的密钥。
2)种子密钥被扩展成为扩展密钥;
3)轮密钥从扩展密钥中取,其中第1轮密钥取扩展密钥的前 Nb 个字,第 2 轮轮密钥取接下来的 Nb 个字,如此下去。
密钥扩展
1)用一个 4 字节字元素的一维数组 W[Nb*(Nr+1)]表示扩展密钥。
2)数组中最开始的 Nk 个字为种子密钥。
3)其它的字由它前面的字经过递归处理后得到。
总的来说递归方式如下:
1)如果i不是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁W[i-1]
2)如果i是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁T(W[i-1])
其中,T是一个有点复杂的函数。函数T由3部分组成:字循环、字节代换和轮常量异或,这3部分的作用分别如下。
a)字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0, b1, b2, b3]变换成[b1,b2,b3,b0]。
b)字节代换:对字循环的结果使用S盒进行字节代换。
c)轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
轮常量Rcon[j]是一个字,其值见下表。
AES的解密算法与加密算法类似(称为逆加密算法),主要区别在于轮密钥要逆序使用,四个基本运算都有对应的逆变换。
AES解密的基本运算中除轮密钥加AddRoundKey不变外(实际上按位异或操作的逆变换是其本身),其余的字节代替SubBytes、行移位ShiftRows、列混淆MixColumns都要进行求逆变换,分别为InvSubBytes、InvShiftRows、InvMixColumns
整体代码:
class AesCryption:
# 字节替换s盒
s_box = {
0: ['0x63', '0x7c', '0x77', '0x7b', '0xf2', '0x6b', '0x6f', '0xc5', '0x30', '0x01', '0x67', '0x2b',
'0xfe', '0xd7', '0xab', '0x76'],
1: ['0xca', '0x82', '0xc9', '0x7d', '0xfa', '0x59', '0x47', '0xf0', '0xad', '0xd4', '0xa2', '0xaf', '0x9c',
'0xa4', '0x72', '0xc0'],
2: ['0xb7', '0xfd', '0x93', '0x26', '0x36', '0x3f', '0xf7', '0xcc', '0x34', '0xa5', '0xe5', '0xf1', '0x71',
'0xd8', '0x31', '0x15'],
3: ['0x04', '0xc7', '0x23', '0xc3', '0x18', '0x96', '0x05', '0x9a', '0x07', '0x12', '0x80', '0xe2', '0xeb',
'0x27', '0xb2', '0x75'],
4: ['0x09', '0x83', '0x2c', '0x1a', '0x1b', '0x6e', '0x5a', '0xa0', '0x52', '0x3b', '0xd6', '0xb3', '0x29',
'0xe3', '0x2f', '0x84'],
5: ['0x53', '0xd1', '0x00', '0xed', '0x20', '0xfc', '0xb1', '0x5b', '0x6a', '0xcb', '0xbe', '0x39', '0x4a',
'0x4c', '0x58', '0xcf'],
6: ['0xd0', '0xef', '0xaa', '0xfb', '0x43', '0x4d', '0x33', '0x85', '0x45', '0xf9', '0x02', '0x7f',
'0x50', '0x3c', '0x9f', '0xa8'],
7: ['0x51', '0xa3', '0x40', '0x8f', '0x92', '0x9d', '0x38', '0xf5', '0xbc', '0xb6', '0xda', '0x21',
'0x10', '0xff', '0xf3', '0xd2'],
8: ['0xcd', '0x0c', '0x13', '0xec', '0x5f', '0x97', '0x44', '0x17', '0xc4', '0xa7', '0x7e', '0x3d',
'0x64', '0x5d', '0x19', '0x73'],
9: ['0x60', '0x81', '0x4f', '0xdc', '0x22', '0x2a', '0x90', '0x88', '0x46', '0xee', '0xb8', '0x14',
'0xde', '0x5e', '0x0b', '0xdb'],
10: ['0xe0', '0x32', '0x3a', '0x0a', '0x49', '0x06', '0x24', '0x5c', '0xc2', '0xd3', '0xac', '0x62', '0x91',
'0x95', '0xe4', '0x79'],
11: ['0xe7', '0xc8', '0x37', '0x6d', '0x8d', '0xd5', '0x4e', '0xa9', '0x6c', '0x56', '0xf4', '0xea', '0x65',
'0x7a', '0xae', '0x08'],
12: ['0xba', '0x78', '0x25', '0x2e', '0x1c', '0xa6', '0xb4', '0xc6', '0xe8', '0xdd', '0x74', '0x1f', '0x4b',
'0xbd', '0x8b', '0x8a'],
13: ['0x70', '0x3e', '0xb5', '0x66', '0x48', '0x03', '0xf6', '0x0e', '0x61', '0x35', '0x57', '0xb9', '0x86',
'0xc1', '0x1d', '0x9e'],
14: ['0xe1', '0xf8', '0x98', '0x11', '0x69', '0xd9', '0x8e', '0x94', '0x9b', '0x1e', '0x87', '0xe9', '0xce',
'0x55', '0x28', '0xdf'],
15: ['0x8c', '0xa1', '0x89', '0x0d', '0xbf', '0xe6', '0x42', '0x68', '0x41', '0x99', '0x2d', '0x0f', '0xb0',
'0x54', '0xbb', '0x16']
}
# 逆字节替换s盒
s_I_box = {
0: ['0x52', '0x09', '0x6a', '0xd5', '0x30', '0x36', '0xa5', '0x38', '0xbf', '0x40', '0xa3', '0x9e', '0x81',
'0xf3', '0xd7', '0xfb'],
1: ['0x7c', '0xe3', '0x39', '0x82', '0x9b', '0x2f', '0xff', '0x87', '0x34', '0x8e', '0x43', '0x44', '0xc4',
'0xde', '0xe9', '0xcb'],
2: ['0x54', '0x7b', '0x94', '0x32', '0xa6', '0xc2', '0x23', '0x3d', '0xee', '0x4c', '0x95', '0x0b', '0x42',
'0xfa', '0xc3', '0x4e'],
3: ['0x08', '0x2e', '0xa1', '0x66', '0x28', '0xd9', '0x24', '0xb2', '0x76', '0x5b', '0xa2', '0x49', '0x6d',
'0x8b', '0xd1', '0x25'],
4: ['0x72', '0xf8', '0xf6', '0x64', '0x86', '0x68', '0x98', '0x16', '0xd4', '0xa4', '0x5c', '0xcc', '0x5d',
'0x65', '0xb6', '0x92'],
5: ['0x6c', '0x70', '0x48', '0x50', '0xfd', '0xed', '0xb9', '0xda', '0x5e', '0x15', '0x46', '0x57', '0xa7',
'0x8d', '0x9d', '0x84'],
6: ['0x90', '0xd8', '0xab', '0x00', '0x8c', '0xbc', '0xd3', '0x0a', '0xf7', '0xe4', '0x58', '0x05', '0xb8',
'0xb3', '0x45', '0x06'],
7: ['0xd0', '0x2c', '0x1e', '0x8f', '0xca', '0x3f', '0x0f', '0x02', '0xc1', '0xaf', '0xbd', '0x03', '0x01',
'0x13', '0x8a', '0x6b'],
8: ['0x3a', '0x91', '0x11', '0x41', '0x4f', '0x67', '0xdc', '0xea', '0x97', '0xf2', '0xcf', '0xce', '0xf0',
'0xb4', '0xe6', '0x73'],
9: ['0x96', '0xac', '0x74', '0x22', '0xe7', '0xad', '0x35', '0x85', '0xe2', '0xf9', '0x37', '0xe8', '0x1c',
'0x75', '0xdf', '0x6e'],
10: ['0x47', '0xf1', '0x1a', '0x71', '0x1d', '0x29', '0xc5', '0x89', '0x6f', '0xb7', '0x62', '0x0e', '0xaa',
'0x18', '0xbe', '0x1b'],
11: ['0xfc', '0x56', '0x3e', '0x4b', '0xc6', '0xd2', '0x79', '0x20', '0x9a', '0xdb', '0xc0', '0xfe', '0x78',
'0xcd', '0x5a', '0xf4'],
12: ['0x1f', '0xdd', '0xa8', '0x33', '0x88', '0x07', '0xc7', '0x31', '0xb1', '0x12', '0x10', '0x59', '0x27',
'0x80', '0xec', '0x5f'],
13: ['0x60', '0x51', '0x7f', '0xa9', '0x19', '0xb5', '0x4a', '0x0d', '0x2d', '0xe5', '0x7a', '0x9f', '0x93',
'0xc9', '0x9c', '0xef'],
14: ['0xa0', '0xe0', '0x3b', '0x4d', '0xae', '0x2a', '0xf5', '0xb0', '0xc8', '0xeb', '0xbb', '0x3c', '0x83',
'0x53', '0x99', '0x61'],
15: ['0x17', '0x2b', '0x04', '0x7e', '0xba', '0x77', '0xd6', '0x26', '0xe1', '0x69', '0x14', '0x63', '0x55',
'0x21', '0x0c', '0x7d']
}
# Rcon生成密钥的表
Rcon = {
1: ['0x01', '0x00', '0x00', '0x00'],
2: ['0x02', '0x00', '0x00', '0x00'],
3: ['0x04', '0x00', '0x00', '0x00'],
4: ['0x08', '0x00', '0x00', '0x00'],
5: ['0x10', '0x00', '0x00', '0x00'],
6: ['0x20', '0x00', '0x00', '0x00'],
7: ['0x40', '0x00', '0x00', '0x00'],
8: ['0x80', '0x00', '0x00', '0x00'],
9: ['0x1B', '0x00', '0x00', '0x00'],
10: ['0x36', '0x00', '0x00', '0x00']
}
# 列混淆
Matrix = [
['0x02', '0x03', '0x01', '0x01'],
['0x01', '0x02', '0x03', '0x01'],
['0x01', '0x01', '0x02', '0x03'],
['0x03', '0x01', '0x01', '0x02']
]
# 逆列混淆
InvMatrix = [
['0x0e', '0x0b', '0x0d', '0x09'],
['0x09', '0x0e', '0x0b', '0x0d'],
['0x0d', '0x09', '0x0e', '0x0b'],
['0x0b', '0x0d', '0x09', '0x0e']
]
plaintext = [[], [], [], []] # 存放明文
plaintext1 = [[], [], [], []]
subkey = [[], [], [], []] # 存放密钥
def __init__(self, key): # 构造函数,同时生成密钥
for i in range(4): # 把16进制转成十进制
for j in range(0, 8, 2):
self.subkey[i].append('0x' + key[i * 8 + j:i * 8 + j + 2])
for i in range(4, 44): # 生成密钥
if i % 4 != 0:
tmp = xor_32(self.subkey[i - 1], self.subkey[i - 4])
self.subkey.append(tmp)
else: # 4的倍数的时候执行
tmp1 = self.subkey[i - 1][1:]
tmp1.append(self.subkey[i - 1][0])
tmp1 = self.S_box(tmp1) # 字节代替
tmp1 = xor_32(tmp1, self.Rcon[i / 4]) # 和Rcon异或
self.subkey.append(xor_32(tmp1, self.subkey[i - 4]))
def AddRoundKey(self, round): # 轮密钥加函数
for i in range(4):
self.plaintext[i] = xor_32(self.plaintext[i], self.subkey[round * 4 + i])
def PlainSubBytes(self): # 明文字节代替函数
for i in range(4):
self.plaintext[i] = self.S_box(self.plaintext[i])
def ShiftRows(self): # 移位函数
p1, p2, p3, p4 = self.plaintext[0][1], self.plaintext[1][1], self.plaintext[2][1], self.plaintext[3][1]
self.plaintext[0][1] = p2;
self.plaintext[1][1] = p3;
self.plaintext[2][1] = p4;
self.plaintext[3][1] = p1
p1, p2, p3, p4 = self.plaintext[0][2], self.plaintext[1][2], self.plaintext[2][2], self.plaintext[3][2]
self.plaintext[0][2] = p3;
self.plaintext[1][2] = p4;
self.plaintext[2][2] = p1;
self.plaintext[3][2] = p2
p1, p2, p3, p4 = self.plaintext[0][3], self.plaintext[1][3], self.plaintext[2][3], self.plaintext[3][3]
self.plaintext[0][3] = p4;
self.plaintext[1][3] = p1;
self.plaintext[2][3] = p2;
self.plaintext[3][3] = p3
def S_box(self, row): # s盒函数
a = []
for i in range(4):
a.append(self.s_box[int(row[i][2], 16)][int(row[i][3], 16)])
return a
def S_I_box(self, row): # 逆s盒函数
a = []
for i in range(4):
a.append(self.s_I_box[int(row[i][2], 16)][int(row[i][3], 16)])
return a
def MixColumns(self): # 列混淆函数
for i in range(4):
for j in range(4):
self.plaintext1[i].append(mc(self.Matrix[j], self.plaintext[i]))
def InvShiftRows(self): # 逆移位函数
p1, p2, p3, p4 = self.plaintext[0][1], self.plaintext[1][1], self.plaintext[2][1], self.plaintext[3][1]
self.plaintext[3][1] = p3;
self.plaintext[2][1] = p2;
self.plaintext[0][1] = p4;
self.plaintext[1][1] = p1
p1, p2, p3, p4 = self.plaintext[0][2], self.plaintext[1][2], self.plaintext[2][2], self.plaintext[3][2]
self.plaintext[0][2] = p3;
self.plaintext[1][2] = p4;
self.plaintext[2][2] = p1;
self.plaintext[3][2] = p2
p1, p2, p3, p4 = self.plaintext[0][3], self.plaintext[1][3], self.plaintext[2][3], self.plaintext[3][3]
self.plaintext[0][3] = p2;
self.plaintext[1][3] = p3;
self.plaintext[2][3] = p4;
self.plaintext[3][3] = p1
def InvSubBytes(self): # 逆字节代替
for i in range(4):
self.plaintext[i] = self.S_I_box(self.plaintext[i])
def InvMixColumns(self): # 逆列混淆
for i in range(4):
for j in range(4):
self.plaintext1[i].append(mc(self.InvMatrix[j], self.plaintext[i]))
def AesEncrypt(self, plain): # 加密函数
for i in range(4):
for j in range(0, 8, 2):
self.plaintext[i].append('0x' + plain[i * 8 + j:i * 8 + j + 2]) # 把16进制转化成二进制
self.AddRoundKey(0) # 第一轮密钥加
for i in range(9):
self.PlainSubBytes() # 字节代替
self.ShiftRows() # 行移位
self.MixColumns() # 列混淆
self.plaintext = self.plaintext1 # 把列混淆生成的密钥赋值给plaintext
self.plaintext1 = [[], [], [], []] # 重置
self.AddRoundKey(i + 1)
self.PlainSubBytes() # 最后一轮字节代替
self.ShiftRows() # 最后一轮行移位
self.AddRoundKey(10) # 最后一轮轮密钥加
return Matrixtostr(self.plaintext) # 把二进制转换成诗十六进制
def AesDecrypt(self, cipher): # 解密函数
for i in range(4):
for j in range(0, 8, 2):
self.plaintext[i].append('0x' + cipher[i * 8 + j:i * 8 + j + 2]) # 16进制转成2进制
self.AddRoundKey(10) # 轮密钥加
for i in range(9):
self.InvShiftRows() # 逆行移位
self.InvSubBytes() # 逆字节代替
self.AddRoundKey(9 - i) # 轮密钥加
self.InvMixColumns() # 逆列混淆
self.plaintext = self.plaintext1
self.plaintext1 = [[], [], [], []]
self.InvShiftRows()
self.InvSubBytes()
self.AddRoundKey(0)
return Matrixtostr(self.plaintext) # 把二进制转换成十六进制
def hextobin(word): # 把十六进制转换成二进制
word = bin(int(word, 16))[2:]
for i in range(0, 8 - len(word)): # 补全八位
word = '0' + word
return word
def bintohex(word): # 把二进制转换十六进制
word = hex(int(word, 2))
if len(word) == 4:
return word
elif len(word) < 4:
return word.replace('x', 'x0') # 0x5-->0x05
def xor_32(start, end): # 32位进行异或
a = []
for i in range(0, 4):
xor_tmp = ""
b = hextobin(start[i])
c = hextobin(end[i])
for j in range(8):
xor_tmp += str(int(b[j], 10) ^ int(c[j], 10))
a.append(bintohex(xor_tmp))
return a
def xor_8(begin, end): # 8位异或
xor_8_tmp = ""
for i in range(8):
xor_8_tmp += str(int(begin[i]) ^ int(end[i]))
return xor_8_tmp
def Fa(a, b): # 列混淆中的乘法运算
if a == 1:
return b
elif a == 2:
if b[0] == '0':
b = b[1:] + '0'
else:
b = b[1:] + '0'
b = xor_8(b, '00011011')
return b
elif a == 3:
tmp_b = b
if b[0] == '0':
b = b[1:] + '0'
else:
b = b[1:] + '0'
b = xor_8(b, '00011011')
return xor_8(b, tmp_b)
elif a == 9:
tmp_b = b
return xor_8(tmp_b, Fa(2, Fa(2, Fa(2, b))))
elif a == 11:
tmp_b = b
return xor_8(tmp_b, xor_8(Fa(2, Fa(2, Fa(2, b))), Fa(2, b)))
elif a == 13:
tmp_b = b
return xor_8(tmp_b, xor_8(Fa(2, Fa(2, Fa(2, b))), Fa(2, Fa(2, b))))
elif a == 14:
return xor_8(Fa(2, b), xor_8(Fa(2, Fa(2, Fa(2, b))), Fa(2, Fa(2, b))))
def mc(s1, s2): # 列混淆中的矩阵乘法
result = []
s3 = []
for i in range(4):
s3.append(hextobin(s2[i]))
for i in range(4):
result.append(Fa(int(s1[i], 16), s3[i]))
for i in range(3):
result[0] = xor_8(result[0], result[i + 1])
return bintohex(result[0])
def Matrixtostr(matrix): # 矩阵转成字符串
result = ""
for i in range(4):
for j in range(4):
result += matrix[i][j][2:]
return result
加密代码:
from AES import AesCryption
key = input("key = ")
plain = input("plain = ")
# cipher = input("cipher = ")
aesencrypt = AesCryption(key)
print('密钥是:'+key)
print('明文是:'+plain)
print('密文是:'+aesencrypt.AesEncrypt(plain))
加密实例:
key = 000102030405060708090a0b0c0d0e0f
plain = 00112233445566778899aabbccddeeff
密钥是:000102030405060708090a0b0c0d0e0f
明文是:00112233445566778899aabbccddeeff
密文是:69c4e0d86a7b0430d8cdb78070b4c55a
Process finished with exit code 0
解密代码:
from AES import AesCryption
key = input("key = ")
# plain = input("plain = ")
cipher = input("cipher = ")
aesencrypt = AesCryption(key)
print('密钥是:'+key)
print('密文是:'+cipher)
print('明文是:'+aesencrypt.AesDecrypt(cipher))
解密实例:
key = 000102030405060708090a0b0c0d0e0f
cipher = 69c4e0d86a7b0430d8cdb78070b4c55a
密钥是:000102030405060708090a0b0c0d0e0f
密文是:69c4e0d86a7b0430d8cdb78070b4c55a
明文是:00112233445566778899aabbccddeeff
Process finished with exit code 0