原理参考:https://blog.csdn.net/qq_27570955/article/details/52442092
流程图大概如图所示:
原理搞懂之后接下来先做一些准备工作,先创建如下文件(理由后面提到)miwen.txt是我最后用来存生成的密文的,可以不建:
每个文件的内容分别如下:
E_extend.txt
32,1,2,3,4,5,
4,5,6,7,8,9,
8,9,10,11,12,13,
12,13,14,15,16,17,
16,17,18,19,20,21,
20,21,22,23,24,25,
24,25,26,27,28,29,
28,29,30,31,32,1
IP.txt:
58,50,42,34,26,18,10,2,
60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,
64,56,48,40,32,24,16,8,
57,49,41,33,25,17, 9,1,
59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,
63,55,47,39,31,23,15,7,
IP-1.txt:
40,8,48,16,56,24,64,32,
39,7,47,15,55,23,63,31,
38,6,46,14,54,22,62,30,
37,5,45,13,53,21,61,29,
36,4,44,12,52,20,60,28,
35,3,43,11,51,19,59,27,
34,2,42,10,50,18,58,26,
33,1,41,9,49,17,57,25,
Key.txt:
1,1,0,1,0,0,0,1,
0,1,0,1,1,0,0,1,
0,1,0,0,1,0,0,0,
1,0,1,0,0,0,0,0,
0,0,1,0,1,0,0,1,
0,1,0,1,0,0,0,1,
1,1,1,1,0,1,1,1,
0,1,1,0,1,0,1,1,
mingwen.txt:
0,1,1,1,1,0,0,0,
0,0,1,0,1,0,0,0,
1,1,1,0,1,0,0,0,
1,1,0,0,1,0,1,0,
0,1,0,0,0,0,1,1,
1,1,1,1,0,1,1,1,
1,1,0,1,1,0,0,0,
0,0,1,1,0,0,1,0,
P.txt:
16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,
2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25,
PC-1.txt:
57,49,41,33,25,17,9,
1,58,50,42,34,26,18,
10,2,59,51,43,35,27,
19,11,3,60,52,44,36,
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14,6,61,53,45,37,29,
21,13,5,28,20,12,4,
PC-1-Ci.txt:
57,49,41,33,25,17,9,
1,58,50,42,34,26,18,
10,2,59,51,43,35,27,
19,11,3,60,52,44,36,
PC-1-Di.txt:
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14,6,61,53,45,37,29,
21,13,5,28,20,12,4,
PC-2.txt:
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32,
S_box1.txt:
14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,
0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,
4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,
15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13,
S_box2.txt:
15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,
3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,
0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,
13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9,
S_box3.txt:
10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,
13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,
13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,
1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12,12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,
10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,
9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,
4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13,
S_box4.txt:
7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,
13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,
10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,
3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14,
S_box5.txt:
2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,
14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,
4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,
11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3,
S_box6.txt:
12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,
10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,
9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,
4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13,
S_box7.txt:
4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,
13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,
1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,
6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12,
S_box8.txt:
13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,
1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,
7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,
2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11,
由于我们在加密过程中,需要读取很多的矩阵(或者说是二维数组),例如s盒就有八个,所以我们首先要解决的是这些矩阵如何读到我们的程序中,我才用了文件读取的方式,将所有需要的矩阵分别存在一个文件里,每次需要的时候就读取文件,定义一个my_read()函数,参数有文件名和矩阵的行列数,将读取到的文件以二维数组(列表套列表)返回:
def my_read(filename,row,col): #读取各种矩阵----参数为文件名和目标矩阵的行列数
sz = [[0 for i in range(col)]for i in range(row)]
with open(filename,"r") as s1:
lists = s1.readlines()
for i in range(len(sz)):
lists[i] = lists[i].split(',') #以 , 将字符串分开
for j in range(len(sz[0])):
sz[i][j] = lists[i][j]
return sz #返回一个该矩阵的二维数组
先从密钥入手,流程图大概如下所示,所以第一步要通过PC-1移位得到C0,D0,为了使主函数简单,逻辑性强,我们也定义一个create_L0R0()函数实现:
def create_L0R0(): #生成L0,R0
mingwen = my_read("mingwen.txt",8,8) #读取明文
rep = my_read("IP.txt",8,8) #读取置换矩阵
rep_mingwen = replace(mingwen,rep,8,8) #进行初始置换
L0 = []
R0 = []
for i in range(4): #得到c0
for j in range(8):
#print(rep[i][j],end = ' ') #检查数值
x,y = get_xy(int(rep[i][j]),8,8) #检查坐标
L0.extend(rep_mingwen[x][y])
for i in range(4,8): #得到d0
for j in range(8):
x,y = get_xy(int(rep[i][j]),8,8) #检查坐标
R0.extend(rep_mingwen[x][y])
#print(L0)
#print(R0)
return [L0,R0]
其中用到的get_xy()函数是用来把一个整型数字(表示位置)变成我们需要的二维数组的坐标。
eg:如果我们想知道第42个位置的数在一个7行8列的二维数组中的坐标,可以调用x,y = get_xy(42,7,8)来获得。具体函数实现如下:
def get_xy(n,row,col): #n为要转化为坐标的数值
if n % col == 0:
x = int(n/col) -1
y = col - 1
else:
x = int(n/col)
y = n%col - 1
return [x,y]
replace(temp,rep,row,col)函数如下:
def replace(temp,rep,row,col): #定义一个置换函数,参数temp为要替换的矩阵,file为替换矩阵,row列,col行
new_temp = [[0 for i in range(col)]for i in range(row)] #用来保存置换后的矩阵并作为返回值
for i in range(len(temp)):
for j in range(len(temp[0])):
#x = int(int(rep[i][j])/8)
#y = int(rep[i][j])%8
x,y = get_xy(int(rep[i][j]),row,col)
##输出坐标
#if y == 0:
# print(x,y,end = '|')
#else :
# print(x,y,end = '|')
#输出置换后矩阵
if int(rep[i][j])%8 == 0:
new_temp[i][j] = temp[x-1][7]
#print(temp[x-1][7],end = ' ')
else:
new_temp[i][j] = temp[x][y]
#print(temp[x][y],end = ' ')
#print('')
return new_temp[:]
得到C0,D0后,我们在写一个函数child_key()一次性获取所有子密钥:
def child_key(): #生成k1~k16的子密钥
key = my_read("Key.txt",8,8) #读取64位key
c = []
d = []
k = []
c0,d0 = create_c0d0(key) #得到c0,d0
num = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1] #设置第n次循环左移位数
for i in range(16): #将生成k1~k16的子密钥存放在列表k中
c0 = my_move(c0,num[i])
d0 = my_move(d0,num[i])
k1 = []
k1.extend(c0)
k1.extend(d0)
pc_2 = my_read("PC-2.txt",8,6)
temp = [] #暂时存放56位子密钥
for m in range(8):
for n in range(6):
temp.append(k1[int(pc_2[m][n])-1])
k.append(temp)
return k[:] #k位16x48的子密钥二维数组
其中的my_move(list,n)函数是实现移位的,位数以n为参数传入:
def my_move(list,n): #循环移位,n为移位数
for i in range(n):##左移
list.insert(len(list),list[0])
list.remove(list[0])
return list
到此之外,子密钥k1-k16已经全部获取,开启下一段征程----处理明文
首先和处理密钥一样的思路,先获取L0,R0:
def create_L0R0(): #生成L0,R0
mingwen = my_read("mingwen.txt",8,8) #读取明文
rep = my_read("IP.txt",8,8) #读取置换矩阵
rep_mingwen = replace(mingwen,rep,8,8) #进行初始置换
L0 = []
R0 = []
for i in range(4): #得到c0
for j in range(8):
x,y = get_xy(int(rep[i][j]),8,8) #检查坐标
L0.extend(rep_mingwen[x][y])
for i in range(4,8): #得到d0
for j in range(8):
x,y = get_xy(int(rep[i][j]),8,8) #检查坐标
R0.extend(rep_mingwen[x][y])
return [L0,R0]
查看流程图,发现我们还有一个f函数没有完成,干!下面是f函数的大概流程:
我们分别定义函数E_extend(R0)、yihuo(a,c)、create_B(K,ERi,num)、get_bi(B)、get_lastR(K,R0,num)分别来实现E扩展、异或运算、得到B、得到B0-B7、经过s盒并进行P置换得到last_R的功能
def E_extend(R0): #将32位Ri扩展位48位
Ri = []
e = my_read("E_extend.txt",8,6)
for m in range(len(e)):
for n in range(len(e[0])):
Ri.append(R0[int(e[m][n])-1])
#print(Ri)
return Ri
def yihuo(a,c): #进行异或运算
B = []
for n in range(len(a)):
b = []
if int(a[n]) == int(c[n]):
b.append(0)
else:
b.append(1)
B.extend(b)
return B
def create_B(K,ERi,num): #进行异或运算,K为48位子密钥列表,ERi为48位扩展的Ri,num是轮数
B = []
for m in range(len(K)):
b = []
for n in range(len(K[0])):
if K[m][n] == ERi[n]:
b.append(0)
else:
b.append(1)
B.append(b)
#print(B)
return B[num] #返回值为B0~B7的集合列表B
def get_bi(B):
b0 = ''
b1 = ''
b2 = ''
b3 = ''
b4 = ''
b5 = ''
b6 = ''
b7 = ''
for i in range(6):
b0 = b0 + str(B[i])
for i in range(6,12):
b1 = b1 + str(B[i])
for i in range(12,18):
b2 = b2 + str(B[i])
for i in range(18,24):
b3 = b3 + str(B[i])
for i in range(24,30):
b4 = b4 + str(B[i])
for i in range(30,36):
b5 = b5 + str(B[i])
for i in range(36,42):
b6 = b6 + str(B[i])
for i in range(42,48):
b7 = b7 + str(B[i])
return [b0,b1,b2,b3,b4,b5,b6,b7]
def get_lastR(K,R0,num): #num为轮数
ERi = E_extend(R0) #E扩展
B = create_B(K,ERi,num) #num轮数用来控制Ki
b0,b1,b2,b3,b4,b5,b6,b7 = get_bi(B)
b0 = cut_bi(b0,0)
b1 = cut_bi(b1,1)
b2 = cut_bi(b2,2)
b3 = cut_bi(b3,3)
b4 = cut_bi(b4,4)
b5 = cut_bi(b5,5)
b6 = cut_bi(b6,6)
b7 = cut_bi(b7,7)
last_R = []
last_R.extend(b0)
last_R.extend(b1)
last_R.extend(b2)
last_R.extend(b3)
last_R.extend(b4)
last_R.extend(b5)
last_R.extend(b6)
last_R.extend(b7)
return last_R
其中上述用到的函数还有
read_s(num)–用来实现读取S盒,n为读取的S盒盒数;
cut_bi()–将bi过s盒在返回,6位变成4位
def read_s(num):
if num+1 == 1:
s = my_read("S_box1.txt",4,16)
elif num+1 == 2:
s = my_read("S_box2.txt",4,16)
elif num+1 == 3:
s = my_read("S_box3.txt",4,16)
elif num+1 == 4:
s = my_read("S_box4.txt",4,16)
elif num+1 == 5:
s = my_read("S_box5.txt",4,16)
elif num+1 == 6:
s = my_read("S_box6.txt",4,16)
elif num+1 == 7:
s = my_read("S_box7.txt",4,16)
else:
s = my_read("S_box8.txt",4,16)
return s
def cut_bi(b,n):
x = my_int2(b[0],b[5])
y = my_int4(b[1],b[2],b[3],b[4])
s = read_s(n+1)
s =[]
temp = read_s(n)
s.append(my_bin(int(temp[x][y])))
return s
还有三个函数my_int4(a1,a2,a3,a4)、my_int2(a1,a2)、my_bin(a)是实现数据转换的,前两个是在读取s盒时,将字符串转化为S盒的坐标,第三个时将十进制整数转化为字符串形式的二进制数。
def my_int4(a1,a2,a3,a4):
a = a1 + a2 + a3 + a4
if a == '0000':
a = int(0)
elif a == '0001':
a = int(1)
elif a == '0010':
a = int(2)
elif a == '0011':
a = int(3)
elif a == '0100':
a = int(4)
elif a == '0101':
a = int(5)
elif a == '0110':
a = int(6)
elif a == '0111':
a = int(7)
elif a == '1000':
a = int(8)
elif a == '1001':
a = int(9)
elif a == '1010':
a = int(10)
elif a == '1011':
a = int(11)
elif a == '1100':
a = int(12)
elif a == '1101':
a = int(13)
elif a == '1110':
a = int(14)
else:
a = int(15)
return a
def my_int2(a1,a2):
a = a1 + a2
if a == '00':
a = int(0)
elif a == '01':
a = int(1)
elif a == '10':
a = int(2)
else:
a = int(3)
return a
def my_bin(a):
if a == 0:
a = '0000'
elif a == 1:
a = '0001'
elif a == 2:
a = '0010'
elif a == 3:
a = '0011'
elif a == 4:
a = '0100'
elif a == 5:
a = '0101'
elif a == 6:
a = '0110'
elif a == 7:
a = '0111'
elif a == 8:
a = '1000'
elif a == 9:
a = '1001'
elif a == 10:
a = '1010'
elif a == 11:
a = '1011'
elif a == 12:
a = '1100'
elif a == 13:
a = '1101'
elif a == 14:
a = '1110'
else:
a = '1111'
return a
最后还有一个函数change(temp)是实现格式化控制的:
def change(temp):
list = []
for m in range(len(temp)):
for n in range(len(temp[0])):
list.append(temp[m][n])
return list
现在我们得到了L16,R16,距离明文还差最后一步逆置换,就在主函数中实现好了,毕竟我们已经定义了18个子函数(太多啦!)
K = child_key() #获取k1~k16的子密钥存依次放在列表K中
L0,R0 = create_L0R0() #获取L0,R0
for num in range(16): #有限月读
L = R0
temp = get_lastR(K,R0,num)
temp = change(temp) #改变一下格式,使temp和L0格式一样,方便实现异或运算
R = yihuo(L0,temp)
L0 = L
R0 = R
mw = [] #存放最终的L,R
mw.extend(L)
mw.extend(R)
rep = my_read("IP-1.txt",8,8) #进行最后一步逆置换并输出密文
miwen = []
for m in range(len(rep)):
for n in range(len(rep[0])):
print(mw[int(rep[m][n])-1],end = ' ')
print('')