AES加密算法是一种对称加密算法,它对原始数据进行加密,生成密文,只有知道密钥和算法,才能解密出原始数据。
本文将介绍AES加密算法的实现,并使用python语言实现AES加密和解密。

基础知识

AES基础知识及加解密算法流程pdf下载

python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#AES算法,密钥长度选择128位,轮数为10,明文和密钥采用UTF-8编码(一个字符代表一个字节,chr与ord转化即可)
#密文采用十六进制编码。
import numpy as np
import constant #constant 存有各种常量:S盒S_box,轮常量Rcon,对数表Log_table,
#反对数表Antilog_table,列混淆加密时的常数矩阵 mix_columns_en_matrix 等

#字节代换(S盒变换)
def subbytes(tai): #参数传入tai,四乘四的矩阵,元素为十六进制字符串
tai_tem=[[0]*4 for _ in range(4)] #储存临时tai
for i in range(4): #对每个元素进行查表替换
for j in range(4):
row=int(tai[i][j],16)//16 #获得S盒的行序号
column=int(tai[i][j],16)%16 #获得S盒的列序号
tai_tem[i][j]='0x'+hex(constant.S_box[row][column])[2:].zfill(2) #S盒替换,
#并将元素转化为十六进制字符串型,和初始元素类型一致
tai_tem=np.array(tai_tem) #转化为矩阵
return tai_tem #字节代换完成,返回新tai

#逆字节代换(逆S盒变换)
def invsubbytes(tai): #参数传入tai,四乘四的矩阵,元素为十六进制字符串
tai_tem=[[0]*4 for _ in range(4)] #储存临时tai
for i in range(4): #对每个元素进行查表替换
for j in range(4):
row=int(tai[i][j],16)//16 #获得S盒的行序号
column=int(tai[i][j],16)%16 #获得S盒的列序号
tai_tem[i][j]='0x'+hex(constant.Inv_Sbox[row][column])[2:].zfill(2) #逆S盒替换,
#并将元素转化为十六进制字符串型,和初始元素类型一致
tai_tem=np.array(tai_tem) #转化为矩阵
return tai_tem #字节代换完成,返回新tai

#轮密钥生成中的T函数
def T(matrix,i,rcon_matrix): #传入的i值用于判断轮数,以找到和第几轮的轮常量进行异或
matrix = np.roll(matrix, -1) #循环左移一位
for j in range(len(matrix)): #对每个元素进行S表替换
j_int=int(matrix[j],16) #j_int存放每个元素的整数值,以方便获得索引调用S_box[][]
row=j_int//16 #获得行索引
column=j_int%16 #获得列索引
matrix[j]='0x'+hex(constant.S_box[row][column])[2:].zfill(2) #S盒替换
# matrix[j]=constant.S_box[row][column] #其实每个元素获得其int型即可,
#因为异或操作函数只能进行元素都是整型的两个矩阵
# matrix=matrix.astype(int) #发现还是字符型,搜索得知,需要重新定义矩阵数组的类型,
# 否则无法进行异或操作,这里将字符型转化为整型,#才能彻底变换元素的类型
w_union_str='0x'+"".join([x[2:].zfill(2) for x in matrix]) #将matrix中的元素['0x12','0x34']
#合成为['0x1234']存入w_union_str,仅举例说明
w_union=np.array([w_union_str]) #转化为矩阵
w_union_int=np.array([int(x,16) for x in w_union]) #将w_union中元素转化为整型,
#w_union_int=array([907426859], dtype=int32)
w_union_int=np.bitwise_xor(w_union_int,rcon_matrix[i//4 - 1]) #将w_union_int与轮常数异或,
#w_union_int=array([924204075], dtype=int32)
w_int_hex=np.array(['0x'+hex(x)[2:].zfill(8) for x in w_union_int]) #w_int_hex=array(['0x37163c2b'])
w_int_hex=w_int_hex[0][2:] #w_int_hex='37163c2b'
matrix=np.array([f'0x{w_int_hex[j:j+2]}' for j in range(0, len(w_int_hex), 2)])
#matrix=array(['0x37', '0x16', '0x3c', '0x2b'], dtype='<U4')
matrix=np.array([int(x,16) for x in matrix]) #将matrix中每一位元素用十进制数存放
return matrix

#轮密钥生成
def subkey_generation(key):
"""
密钥长度为128位(16字节),轮数为10
"""
if len(key) != 16:
raise ValueError('Key length should be 128 bits(16 bytes).') #判断密钥长度是否为16字节,128位
key_blocks = [key[i] for i in range(0,len(key))] #先将密钥存入列表中
key_schedule = [[0]*4 for _ in range(4)] #初始化密钥初始矩阵
for i in range(4):
for j in range(4):
if key_blocks[i+j*4].isprintable(): #将一排十六进制密钥,竖着一列一列存到矩阵中
key_schedule[i][j] = '0x' + format(ord(key_blocks[i+j*4]), '02x')
#初始密钥矩阵列表,将每个字节以十六进制形式存放
key_schedule = np.array(key_schedule) #转化为矩阵
###########################################################################
# =============================================================================
# #测试第5-3章-AES.ppt P60例二 密码书 P125页
# # 十六进制字符串序列
# key_blocks_ori = ['2b', '7e', '15', '16', '28', 'ae', 'd2', 'a6', 'ab', 'f7', '15', '88', '09', 'cf', '4f', '3c']
# # 将十六进制字符串转为含有 0x 前缀的十六进制值
# key_blocks = ['0x' + x for x in key_blocks_ori]
# # 将其转化为 4x4 矩阵
# for i in range(4):
# for j in range(4):
# if key_blocks[i+j*4].isprintable(): #将一排十六进制密钥,竖着一列一列存到矩阵中
# key_schedule[i][j] = key_blocks[i+j*4] #初始密钥矩阵列表,将每个字节以十六进制形式存放
# key_schedule = np.array(key_schedule) #转化为矩阵
# =============================================================================
###########################################################################
w=[[0]*4 for _ in range(44)] #初始化存放所有操作完成后的子密钥的列表,基础元素为十六进制数
w[0]=key_schedule[:,0] #取出初始密钥矩阵的第一列作为w[0] array(['0x3C', '0xA1', '0x0B', '0x21']
w[1]=key_schedule[:,1] #取出初始密钥矩阵的第二列作为w[1]
w[2]=key_schedule[:,2] #取出初始密钥矩阵的第三列作为w[2]
w[3]=key_schedule[:,3] #取出初始密钥矩阵的第一四作为w[3]
rcon_matrix=np.array(constant.Rcon) #将轮常数列表转化为矩阵
for i in range(4,44):
if i%4!=0 : #i不是4的倍数时
wi1_int=np.array([int(x,16) for x in w[i-1]])
wi4_int=np.array([int(x,16) for x in w[i-4]])
w[i]=np.bitwise_xor(wi1_int,wi4_int)
w[i]=np.array(['0x'+hex(x)[2:].zfill(2) for x in w[i]])
#将w[i]中的元素转化为16进制存放
else : #i是4的倍数时
wi4_int=np.array([int(x,16) for x in w[i-4]])
#将w[i-4]中的['0x10',...,'0x11'],转化为整数存放[16,...,17],仅举例说明用
w[i]=np.bitwise_xor(wi4_int,T(w[i-1],i,rcon_matrix))
#将w[i-4]与经过T函数操作过的w[i-1]的对应位进行异或操作得到w[i],此时w[i]中元素类型为整型
w[i]=np.array(['0x'+hex(x)[2:].zfill(2) for x in w[i]]) #将w[i]中的元素转化为16进制存放
return w #返回生成的轮密钥

#明文转化为矩阵
def plain_tran(plaintext):
plain_blocks = [plaintext[i] for i in range(0,len(plaintext))] #先将明文存入列表中
plain_schedule = [[0]*4 for _ in range(4)] #初始化明文初始矩阵
for i in range(4):
for j in range(4):
if plain_blocks[i+j*4].isprintable(): #将一排十六进制明文,竖着一列一列存到矩阵中
plain_schedule[i][j] = '0x' + format(ord(plain_blocks[i+j*4]), '02x')
#初始明文矩阵列表,将每个字节以十六进制形式存放
plain_schedule = np.array(plain_schedule) #转化为矩阵
###########################################################################
# =============================================================================
# #测试 PPT P58 密码书 P127
# plain_schedule = np.array([['0x32','0x88','0x31','0xe0'],['0x43','0x5a','0x31','0x37'],['0xf6','0x30','0x98','0x07'],['0xa8','0x8d','0xa2','0x34']])
# =============================================================================
###########################################################################
return plain_schedule

#AES加密算法
def aes_encode(plain_schedule,w):
tai=[] #存放tai
tai.append(plain_schedule) #初始tai为明文十六进制编码的四乘四矩阵
w_array=np.array(w) #w_array存放矩阵形式的w
subkey_w = [w_array[i:i + 4].T for i in range(0, len(w), 4)]
#subkey_w中存放了初始密钥和十轮子密钥,共十一个密钥,每个密钥是四乘四矩阵形式

#初始轮密钥加变换,就是相应位置的元素进行异或操作
tai0_int=np.array([[int(x,16) for x in row] for row in plain_schedule])
subkey_int0=np.array([[int(x,16) for x in row] for row in subkey_w[0]])
tai.append(np.bitwise_xor(tai0_int,subkey_int0))
tai[1]=np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai[1]])
#将tai[i]中的元素转化为16进制存放

#开始循环十轮加密
for i in range(10):
#字节代换-SubBytes
tai.append(subbytes(tai[len(tai)-1]))
#行移位-ShiftRows
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
tai_tem = [row[i:] + row[:i] for i, row in enumerate(tai_tem_int)]
#实现从上到下每行左移零位,一位,两位,三位
tai_tem = np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)
#列混淆-MixColumns
if i!=9:
tai_tem=np.array(list(tai_tem))
#为什么不直接使用tai_tem呢,因为直接使用,会导致tai中append进去的tai_tem也发生变化,
#所以在此进行拷贝一个副本tai_tem进行操作
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
tai_tem_matrix=np.array(tai_tem_int)
for j in range(4):
for k in range(4):
tai_tem[j][k]=row_cheng_columns(constant.mix_columns_en_matrix[j],list(tai_tem_matrix[:,k]) )
tai_tem = np.array([['0x'+hex(int(x))[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)
#轮密钥加
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
subkey_int=np.array([[int(x,16) for x in row] for row in subkey_w[i+1]])
tai_tem=np.bitwise_xor(tai_tem_int,subkey_int)
tai_tem=np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)

#加密结果为ciphertext
ciphertext_matrix=tai[len(tai)-1].T
ciphertext=''
for i in range(4):
for j in range(4):
ciphertext+=str(ciphertext_matrix[i][j][2:])
return ciphertext

#列混淆常数矩阵乘以tai 实现 行列表[0x01,0x02,0x03,0x04]×列列表[0x3a,0x32,0x12,0x44]在GF(2**8)域上的运算,
#通过查对数表,反对数表
def row_cheng_columns(row,column):
a=[]
for i in range(4):
# 如果当前元素为 0x00,直接跳过
if row[i] == 0x00 or column[i] == 0x00:
#此种特殊情况为:aes解密时,若出现查对数表0行0列的特殊情况时,直接将该乘积结果赋为0
a.append(0x00)
continue
log_row_row_num=row[i]//16
log_row_column_num=row[i]%16
log_column_row_num=column[i]//16
log_column_column_num=column[i]%16
tem=(constant.Log_table[log_row_row_num][log_row_column_num]+constant.Log_table[log_column_row_num][log_column_column_num])
if tem>255:
#此种特殊情况为,当查反对数表时出现行列值大于15的特殊情况,大于了GF(2**8),
#需要 -255 或者 %255 再进行查反对数表
tem=tem%255
result_row=tem//16
result_column=tem%16
tem=constant.Antilog_table[result_row][result_column]
a.append(tem)
result=0
for num in a:
result^=num
return result

#密文转化为矩阵
def cipher_tran(ciphertext):
cipher_schedule=[[0]*4 for _ in range(4)]
cipher_list=[ciphertext[i:i+2] for i in range(0,len(ciphertext),2)]
for i in range(4):
for j in range(4):
cipher_schedule[i][j]='0x'+cipher_list[i+j*4]
cipher_schedule=np.array(cipher_schedule)
return cipher_schedule

#解密算法
def aes_decode(cipher_schedule,w):
tai=[] #存放tai
tai.append(cipher_schedule) #初始tai为密文十六进制编码的四乘四矩阵
w_array=np.array(w) #w_array存放矩阵形式的w
subkey_w = [w_array[i:i + 4].T for i in range(0, len(w), 4)]
#subkey_w中存放了初始密钥和十轮子密钥,共十一个密钥,每个密钥是四乘四矩阵形式
subkey_w = subkey_w[::-1] #将十一个密钥倒序排放,用于解密

#初始轮密钥加变换,就是相应位置的元素进行异或操作
tai0_int=np.array([[int(x,16) for x in row] for row in tai[0]])
subkey_int10=np.array([[int(x,16) for x in row] for row in subkey_w[0]])
tai.append(np.bitwise_xor(tai0_int,subkey_int10))
tai[1]=np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai[1]])
#将tai[i]中的元素转化为16进制存放

#开始循环十轮解密
for i in range(10):
#逆行移位-InvShiftRows
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
tai_tem = [row[-i:] + row[:-i] for i, row in enumerate(tai_tem_int)]
#实现从上到下每行右移零位,一位,两位,三位
tai_tem = np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)
#逆字节代换-InvSubBytes
tai.append(invsubbytes(tai[len(tai)-1]))
#轮密钥加
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
subkey_int=np.array([[int(x,16) for x in row] for row in subkey_w[i+1]])
tai_tem=np.bitwise_xor(tai_tem_int,subkey_int)
tai_tem=np.array([['0x'+hex(x)[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)

#逆列混淆-InvMixColumns
if i!=9:
tai_tem=np.array(list(tai_tem)) #为什么不直接使用tai_tem呢,因为直接使用,
#会导致tai中append进去的tai_tem也发生变化,所以在此进行拷贝一个副本tai_tem进行操作
tai_tem_int = [[int(x, 16) for x in row] for row in tai[len(tai)-1]]
tai_tem_matrix=np.array(tai_tem_int)
for j in range(4):
for k in range(4):
tai_tem[j][k]=row_cheng_columns(constant.mix_columns_de_matrix[j],list(tai_tem_matrix[:,k]) )
tai_tem = np.array([['0x'+hex(int(x))[2:].zfill(2) for x in row] for row in tai_tem])
tai.append(tai_tem)

#加密结果为plaintext
plaintext_matrix=tai[len(tai)-1].T
plaintext=''
for i in range(4):
for j in range(4):
plaintext+=chr(int(plaintext_matrix[i][j],16))
return plaintext

#主程序
def main():
while True:
print("==================== AES 加密与解密 ====================")
print("[1] 加密")
print("[2] 解密")
print("[0] 退出")
print("====================================================")
try:
choice = int(input("请输入操作选项 (0, 1 或 2): "))
if choice == 0:
print("\n\n程序已退出。感谢使用 AES 加解密工具!")
break
elif choice == 1:
# 加密流程
plaintext = input("请输入明文 (UTF-8 编码,16 个字符): ")
plain_schedule=plain_tran(plaintext)
key = input("请输入密钥 (UTF-8 编码,16 个字符): ")
w=subkey_generation(key) #w存入轮密钥w[0]-w[3],用来对明文进行初始的轮密钥加变化,
#w[4]-w[7]作为第一轮子密钥,一共十轮,第十轮子密钥为w[40]-w[43]
if not plaintext or not key:
raise ValueError("明文和密钥均不能为空!")
ciphertext = aes_encode(plain_schedule, w)
print("\n加密结果 (十六进制):")
print(ciphertext)
elif choice == 2:
# 解密流程
ciphertext = input("请输入密文 (连续的十六进制): ")
cipher_schedule=cipher_tran(ciphertext)
key = input("请输入密钥 (UTF-8 编码,16 个字符): ")
w=subkey_generation(key) #w存入轮密钥w[0]-w[3],用来对明文进行初始的轮密钥加变化,
#w[4]-w[7]作为第一轮子密钥,一共十轮,第十轮子密钥为w[40]-w[43]
if not ciphertext or not key:
raise ValueError("密文和密钥均不能为空!")
plaintext = aes_decode(cipher_schedule, w)
print("\n解密结果 (UTF-8 编码):")
print(plaintext)
else:
raise ValueError("无效选项")

except ValueError as e:
print(f"输入错误: {e}")
except Exception as e:
print(f"发生错误: {e}")

if __name__ == "__main__":
main()