RIJNDAEL 加密算法
注意:本文实现的AES算法的密钥,明文,密文均为128位,后续可能会添加192位或256位的密钥。函数flag参数默认是1,即加密,在解密调用时改为0即可。
先放个运行效果:
RIJNDAEL算法仍然采用分组密码的一种通用结构:对轮函数实施迭代的结构。只是轮函数结构采用的是代替/置换网络结构(SP结构),没有采用DES的Feistel结构。RIJNDAEL的轮函数由以下三层组成:
- 非线性层:进行非线性S盒变换ByteSub,由16个S盒并置而成,具有混淆的作用;
- 线性混合层:进行行移位变换ShiftRow和列混合变换MixColumn以确保多轮之上的高度扩散;
- 密钥加层:进行轮密钥加变换AddRoundKey,将轮密钥简单地异或到中间状态上 ,实现密钥的加密控制作用。
轮函数
轮函数结构采用的是代替/置换网络结构(SP结构),由S盒变换ByteSub、行移位变换ShiftRow、列混合变换MixColumn、轮密钥加变换AddRoundKey组成。伪代码如下:
Round(State,RoundKey)
{ ByteSub(State);
ShiftRow(State);
MixColumn(State);
AddRoundKey(State,RoundKey);
}
最后一轮的轮函数有所不同:
FinalRound(State,RoundKey)
{ ByteSub(State);
ShiftRow(State);
AddRoundKey(State,RoundKey);
}
(1)S盒变换ByteSub
ByteSub变换是按字节进行的代替变换,它是非线性字节变换,主要按以下两步进行:
1)把字节的逆用它的乘法逆来代替,其中‘00’的逆就是它自己。a(x)b(x) mod m(x) = 1
2)经1)处理后的字节值再进行如下定义的仿射变换:
ByteSub变换相当于DES的S盒。它为加密算法提供非线性,是决定加密算法安全性的关键。为确保加密算法是可逆的,ByteSub变换必须是可逆的。
#输入8位的16进制,也就是一个字
#S盒运算
def SubByte(E,flag=1):#flag默认为1是加密
if flag == 1:
S = ['63', '7c', '77', '7b', 'f2', '6b', '6f', 'c5', '30', '01', '67', '2b', 'fe', 'd7', 'ab', '76',
'ca', '82', 'c9', '7d', 'fa', '59', '47', 'f0', 'ad', 'd4', 'a2', 'af', '9c', 'a4', '72', 'c0',
'b7', 'fd', '93', '26', '36', '3f', 'f7', 'cc', '34', 'a5', 'e5', 'f1', '71', 'd8', '31', '15',
'04', 'c7', '23', 'c3', '18', '96', '05', '9a', '07', '12', '80', 'e2', 'eb', '27', 'b2', '75',
'09', '83', '2c', '1a', '1b', '6e', '5a', 'a0', '52', '3b', 'd6', 'b3', '29', 'e3', '2f', '84',
'53', 'd1', '00', 'ed', '20', 'fc', 'b1', '5b', '6a', 'cb', 'be', '39', '4a', '4c', '58', 'cf',
'd0', 'ef', 'aa', 'fb', '43', '4d', '33', '85', '45', 'f9', '02', '7f', '50', '3c', '9f', 'a8',
'51', 'a3', '40', '8f', '92', '9d', '38', 'f5', 'bc', 'b6', 'da', '21', '10', 'ff', 'f3', 'd2',
'cd', '0c', '13', 'ec', '5f', '97', '44', '17', 'c4', 'a7', '7e', '3d', '64', '5d', '19', '73',
'60', '81', '4f', 'dc', '22', '2a', '90', '88', '46', 'ee', 'b8', '14', 'de', '5e', '0b', 'db',
'e0', '32', '3a', '0a', '49', '06', '24', '5c', 'c2', 'd3', 'ac', '62', '91', '95', 'e4', '79',
'e7', 'c8', '37', '6d', '8d', 'd5', '4e', 'a9', '6c', '56', 'f4', 'ea', '65', '7a', 'ae', '08',
'ba', '78', '25', '2e', '1c', 'a6', 'b4', 'c6', 'e8', 'dd', '74', '1f', '4b', 'bd', '8b', '8a',
'70', '3e', 'b5', '66', '48', '03', 'f6', '0e', '61', '35', '57', 'b9', '86', 'c1', '1d', '9e',
'e1', 'f8', '98', '11', '69', 'd9', '8e', '94', '9b', '1e', '87', 'e9', 'ce', '55', '28', 'df',
'8c', 'a1', '89', '0d', 'bf', 'e6', '42', '68', '41', '99', '2d', '0f', 'b0', '54', 'bb', '16']
else:#逆S盒表
S = ['52', '09', '6a', 'd5', '30', '36', 'a5', '38', 'bf', '40', 'a3', '9e', '81', 'f3', 'd7', 'fb',
'7c', 'e3', '39', '82', '9b', '2f', 'ff', '87', '34', '8e', '43', '44', 'c4', 'de', 'e9', 'cb',
'54', '7b', '94', '32', 'a6', 'c2', '23', '3d', 'ee', '4c', '95', '0b', '42', 'fa', 'c3', '4e',
'08', '2e', 'a1', '66', '28', 'd9', '24', 'b2', '76', '5b', 'a2', '49', '6d', '8b', 'd1', '25',
'72', 'f8', 'f6', '64', '86', '68', '98', '16', 'd4', 'a4', '5c', 'cc', '5d', '65', 'b6', '92',
'6c', '70', '48', '50', 'fd', 'ed', 'b9', 'da', '5e', '15', '46', '57', 'a7', '8d', '9d', '84',
'90', 'd8', 'ab', '00', '8c', 'bc', 'd3', '0a', 'f7', 'e4', '58', '05', 'b8', 'b3', '45', '06',
'd0', '2c', '1e', '8f', 'ca', '3f', '0f', '02', 'c1', 'af', 'bd', '03', '01', '13', '8a', '6b',
'3a', '91', '11', '41', '4f', '67', 'dc', 'ea', '97', 'f2', 'cf', 'ce', 'f0', 'b4', 'e6', '73',
'96', 'ac', '74', '22', 'e7', 'ad', '35', '85', 'e2', 'f9', '37', 'e8', '1c', '75', 'df', '6e',
'47', 'f1', '1a', '71', '1d', '29', 'c5', '89', '6f', 'b7', '62', '0e', 'aa', '18', 'be', '1b',
'fc', '56', '3e', '4b', 'c6', 'd2', '79', '20', '9a', 'db', 'c0', 'fe', '78', 'cd', '5a', 'f4',
'1f', 'dd', 'a8', '33', '88', '07', 'c7', '31', 'b1', '12', '10', '59', '27', '80', 'ec', '5f',
'60', '51', '7f', 'a9', '19', 'b5', '4a', '0d', '2d', 'e5', '7a', '9f', '93', 'c9', '9c', 'ef',
'a0', 'e0', '3b', '4d', 'ae', '2a', 'f5', 'b0', 'c8', 'eb', 'bb', '3c', '83', '53', '99', '61',
'17', '2b', '04', '7e', 'ba', '77', 'd6', '26', 'e1', '69', '14', '63', '55', '21', '0c', '7d']
A = []
e = list(E)#len(e)=8
if len(e) == 8:
for i in range(0,8,2):#每次加2
a = int(e[i],16)#高位16进制转换为10进制
b = int(e[i+1],16)#低位16进制转换为10进制
c = S[a*16+b]#找到对应的16进制
A.append(c)
B = ''.join(A)
else:
for i in range(0, 32, 2): # 每次加2
a = int(e[i], 16) # 高位16进制转换为10进制
b = int(e[i + 1], 16) # 低位16进制转换为10进制
c = S[a * 16 + b] # 找到对应的16进制
A.append(c)
B = ''.join(A)
return B
(2)行移位变换ShiftRow
在ShiftRow变换中,第0行不移位,第1行循环左移1字节,第2行循环左移2字节,第3行循环左移3字节。
#行移位变换函数
def ShiftRow(B,flag=1):
A = list(B)
r0 = []
r1 = []
r2 = []
r3 = []
R = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
for i in range(0,32,2):#每次加2
if i%8 == 0:#处理第0行
a = A[i]#'6'
b = A[i+1]#'3'
a = a + b#'63'
r0.append(a)
elif i%8 == 2:#处理第1行
a = A[i]
b = A[i + 1]
a = a + b
r1.append(a)
elif i%8 == 4:#处理第2行
a = A[i]
b = A[i + 1]
a = a + b
r2.append(a)
else:#处理第3行
a = A[i]
b = A[i + 1]
a = a + b
r3.append(a)
if flag == 1:
r1 = r1[1:] + r1[:1]#第1行循环左移一个字节
r2 = r2[2:] + r2[:2]#第2行循环左移两个字节
r3 = r3[3:] + r3[:3]#第3行循环左移三个字节
else:
r1 = r1[3:] + r1[:3] # 第1行循环左移一个字节
r2 = r2[2:] + r2[:2] # 第2行循环左移两个字节
r3 = r3[1:] + r3[:1] # 第3行循环左移三个字节
for i in range(16):
if i%4 == 0:
R[i] = r0[i//4]
elif i%4 == 1:
R[i] = r1[(i-1)//4]
elif i%4 ==2:
R[i] = r2[(i-2)//4]
else:
R[i] = r3[(i-3)//4]
r = ''.join(R)
#print(r)
return r
(3)列混合变换MixColumn
在MixColumn变换中,把状态的每一列看做GF(2^8)上的多项式,并于一个固定多项式c(x)相乘后模多项式 x^4+1,其中c(x)为:
因为c(x)与x^4+1是互素,从而保证c(x)存在逆多项式d(x),使c(x)d(x)=1 mod x^4+1。只有逆多项式d(x)存在,才能正确解密。
#列混合变换函数
def MixColumn(r,flag=1):
r = list(r)
A0 = []
A1 = []
A2 = []
A3 = []
B0 = [0,0,0,0]
B1 = [0,0,0,0]
B2 = [0,0,0,0]
B3 = [0,0,0,0]
D = [0,0,0,0]
E = [0,0,0,0]
for i in range(0,32,2):
if i < 8:
a = r[i]
b = r[i+1]
a = a + b #a=63
A0.append(a)#A0=[63,e0,63,89]
elif i<16:
a = r[i]
b = r[i + 1]
a = a + b
A1.append(a)#A1=['51','7c','63','63']
elif i < 24:
a = r[i]
b = r[i + 1]
a = a + b
A2.append(a)
else:
a = r[i]
b = r[i + 1]
a = a + b
A3.append(a)
A = [A0,A1,A2,A3]#[['63','e0','63','89'],['51','7c','63','63'],['63','63', 'b7','7c'], ['8e','63','63','b7']]
if flag == 1:#矩阵乘积形式的矩阵
C = [['02', '03', '01', '01'], ['01', '02', '03', '01'],['01', '01', '02', '03'], ['03', '01', '01', '02']]
else:
C = [['0E', '0B', '0D', '09'], ['09', '0E', '0B', '0D'],['0D', '09', '0E', '0B'], ['0B', '0D', '09', '0E']]
B = [B0,B1,B2,B3]
for k in range(4):#循环A
for j in range(4):#循环C
for i in range(4):#两位16进制相乘
if flag == 1:
d = xiangcheng_16(C[j][i],A[k][i])#d为2为16进制
D[i] = d
else:
d = xiangcheng_16_jiemi(C[j][i], A[k][i]) # d为2为16进制
D[i] = d
for i in range(4):#异或后即是[1,1]
if i == 0:
z = D[i]
else:
z = yihuo_16(z,D[i])
B[k][j] = z
for i in range(4):
a = B[i]
b_1 = [str(i) for i in a]
b = ''.join(b_1)
E[i] = b
e = ''.join(E)
return e
(4)轮密钥加变换AddRoundKey
AddRoundKey变换是利用轮密钥对状态进行模2相加的变换。轮密钥长度等于数据块长度。轮密钥根据轮密钥产生算法通过主密钥扩展和选择得到。
#轮密钥加函数,输入状态和密钥,RoundKey是32位字符串
#轮密钥与经过列混合之后的数相加
#RoundKey是32位16进制
def AddRoundKey(State,RoundKey):
k = list(RoundKey)
d = list(State)
E = []
for i in range(32):
a = int(k[i], 16)#16进制转换为10进制
b = int(d[i], 16)
e =a ^ b #10进制可以进行异或
e = format(e,'x')#异或后的值转换为16进制
E.append(e) #16进制以列表存放
E =''.join(E) #转换为字符串
return E
轮密钥产生算法
本文主要针对128位的密钥展开。
轮密钥根据轮密钥产生算法有主密钥产生得到。轮密钥产生分为两步:密钥扩展和轮密钥选择,且遵循以下原则:
- 轮密钥的比特总数为数据块长度与轮数加1 的乘积。例如,对于128位的分组长度和10轮迭代,轮函数的总长度为128*(10+1)=1048位。
- 首先将用户密钥扩展成一个扩展密钥。
- 再从扩展密钥中选出轮密钥:第一个轮密钥由扩展密钥中的前Nb个字组成,第二个轮密钥由接下来的Nb个字组成,以此类推。
(1)密钥扩展
用一个字(四个字节)元素的一维数组W[Nb*(Nr+1)]存储扩展密钥。把主密钥放在数组W最开始的Nk个字中,其他的字由它前面的字经过处理后得到。
符号说明:CipherKey表示主密钥,它是一个有Nk个密钥字的一维数组。W为存储扩展密钥的一维数组。因为是针对128位的主密钥,Nk=4,Nb=4,Nr=10。
针对Nk<=6的密钥扩展策略
KeyExpansion(CipherKey,W)
{ For (I=0;I<Nk;I++)
W[I] = CipherKey[I];
For (I=Nk;I<Nb*(Nr+1);I++)
{ Temp = W[I-1];
IF(I%Nk==0)
Temp=SubByte(Rotl(Temp))⨁Rcon[I/Nk];
W[I] = W[I-Nk]⨁Temp;
}
}
最前面的Nk个字是由主密钥填充的。这之后的每一个字W[I]等于前面的W[I-1]与Nk个位置之前的字W[I-Nk]的异或。如果I是Nk的整数倍,在异或之前要先对W[I-1]进行Rotl变换和SubByte变换,再异或一个轮常数Rcon。
其中Rotl是对一个字里的字节以字节为单位进行循环移位的函数,设W=(A,B,C,D),则Rotl(W)=(B,C,D,A)。
轮常数Rcon与Nk无关,且定义为:
Rcon[i] = (RC[i],'00','00','00')
RC[0] = ‘01’
RC[i] = xtime(RC[i-1])
#在GF(2^8)中,
#a(x)b(x) mod m(x) = 1
#倍增函数xtime(b(x))定义为 x*b(x) mod m(x)
使用轮函数Rcon的目的是为了防止不同轮的轮密钥存在相似性。由于轮函数Rcon是变化的,可使不同轮的轮密钥有明显的不同。
(2)轮密钥选择
轮密钥I由轮密钥缓冲区W[Nb * I]到W[Nb*(I+1)-1]的字组成。
#!!千万要注意格式化输入输出的16进制或者2进制数据的位数
#密钥扩展函数
#CipherKey是一个4字的数组,W是一个44字的数组
#输入的密钥是32位的16进制,也就是要先分成4个字
def KeyExpansion(CipherKey):
C = list(CipherKey)
c = [[],[],[],[]]
for i in range(32):
if i < 8:#每8位放一个列表
c[0].append(C[i])
elif i < 16:
c[1].append(C[i])
elif i < 24:
c[2].append(C[i])
else:
c[3].append(C[i])
for i in range(4):
c[i] = ''.join(c[i])#把c[i]由列表化为字符串,字符串长8位
W = [0 for i in range(44)]
#为了增大效率,将计算得出的倍增函数结果直接存储
#b列表存放计算之后的轮常数Rcon,RC[0]...
b = ['01000000','02000000', '04000000', '08000000', '10000000', '20000000',
'40000000', '80000000', '1b000000', '36000000', '6c000000']
for i in range(4):#前4个字就是首轮密钥
W[i] = c[i]
#开始扩展
for i in range(4,44):
Temp = W[i-1]
if i%4==0:
a = Rotl(Temp)#a为一个8位的字符串,对一个字里的字节以字节为单位进行循环移位的函数
x = SubByte(a)#输入一个8位的16进制,也就是一个字,S盒变换
Temp = yihuo_32(x,b[(i//4)-1])
W[i] = yihuo_32(W[i - 4], Temp)
return W #生成扩展密钥
#两个8位16进制数,即32位2进制的异或运算
def yihuo_32(a0,a1):
A = []
ten_a0 = int(a0,16)
ten_a1 = int(a1, 16)
ten_bin_a0 = '{:032b}'.format(ten_a0)
ten_bin_a1 = '{:032b}'.format(ten_a1)
list_a0 = list(ten_bin_a0)
list_a1 = list(ten_bin_a1)
for i in range(32):
a = int(list_a0[i])^int(list_a1[i])
A.append(a)
c_1 = [str(i) for i in A]
c = ''.join(c_1)
c = int(c, 2)
c = '{:08x}'.format(c)#这一步16进制没规定规格坑死人
return c
#左循环移位函数,输入一个字,以字节为单位左移,输出也是一个字的字符串
#对一个字里的字节以字节为单位进行循环移位的函数
def Rotl(W):
a = list(W)#有八位
a = a[2:] + a[:2]#左移两位16进制数
b = ''.join(a)#重新转换为字符串
return b
解密算法的过程之后有时间再补上,代码已经实现了AES加密和解密。
感想:想写好一篇博客实在是太难,太花费时间了,写了一大堆下来连自己都看不太明白。现在主要是为了记录,后续慢慢修改吧。写得太乱了,欢迎批评。