0%

密码学课程设计之分组密码

密码学课程设计之分组密码

分组密码(block cipher)的数学模型是将明文消息编码表示后的数字(简称明文数字)序列,划分成长度为n的组(可看成长度为n的矢量),每组分别在密钥的控制下变换成等长的输出数字(简称密文数字)序列。

1

DES

基本结构

DES是一个对称密码体制,加密和解密使用同一密钥,有效密钥的长度为56位。DES是一个分组密码算法,分组长度为64位,即对数据进行加密和解密的单位是64位,明文和密文的长度相同。另外,DES使用Feistel结构,具有加解密相似特征。

2

基本原则

DES设计中使用了分组密码设计的两个原则:混淆(confusion)和扩散(diffusion),其目的是抗击敌手对密码系统的统计分析。混淆是使密文的统计特性与密钥的取值之间的关系尽可能复杂化,以使密钥和明文以及密文之间的依赖性对密码分析者来说是无法利用的。扩散的作用就是将每一位明文的影响尽可能迅速地作用到较多的输出密文位中,以便在大量的密文中消除明文的统计结构,并且使每一位密钥的影响尽可能迅速地扩展到较多的密文位中,以防对密钥进行逐段破译。

算法步骤

以上图DES加密为例,解密为逆过程。

1
2
3
64的明文经过初始置换(IP)而被重新排列,并将其分为左右两个分组L0和R0,各为32位。
在密钥的参与下,对左右两个分组进行16轮相同轮函数的迭代,每轮迭代都有置换和代换。注意最后一轮迭代的输出为64位,左半部分和右半部分不进行交换。
最后的预输出再通过逆初始置换(IP-1)产生64位的密文。

加密过程
$$
L_0R_0{\leftarrow}IP(<64位明文>)
$$

$$
L_i{\leftarrow}R_{i-1}
$$

$$
R_i{\leftarrow}L_{i-1}{\bigoplus}F(R_{i-1},K_i)
$$

$$
L_{16}{\leftarrow}L_{15}{\bigoplus}F(R_{15},K_{16})
$$

$$
R_{16}{\leftarrow}R_{15}
$$

$$
<64位密文>{\leftarrow}IP^{-1}(L_{16}R_{16})
$$

解密过程

$$
L_{16}R_{16}{\leftarrow}IP(<64位密文>)
$$

$$
R_{i-1}{\leftarrow}L_{i}{\bigoplus}F(R_{i},K_i)
$$

$$
L_{i-1}{\leftarrow}R_i
$$

$$
L_{0}{\leftarrow}L_{1}{\bigoplus}F(R_{1},K_{1})
$$

$$
R_{0}{\leftarrow}R_{1}
$$

$$
<64位明文>{\leftarrow}IP^{-1}(L_{0}R_{0})
$$

算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#字符串转化为二进制
def str2bin(message):
res = ""
for i in message:
tmp = bin(ord(i))[2:]
for j in range(0,8-len(tmp)):
tmp = '0'+ tmp
res += tmp
return res

#二进制转化为字符串
def bin2str(bin_str):
res = ""
tmp = re.findall(r'.{8}',bin_str)
for i in tmp:
res += chr(int(i,2))
return res

密钥编排

DES的最初64位密钥通过置换选择PC-1得到有效的56位密钥。

经过循环左移和置换选择2 ( PC-2 ) 后分别得到 16 个 48 位子密钥 Ki 用做每一轮的迭代运算。

9

置换选择

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
#密钥的PC-1置换
def change_key1(my_key):
PC_1 = [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]
res = ""
for i in PC_1:
res += my_key[i-1]
return res

#密钥的PC-2置换
def change_key2(my_key):
PC_2 = [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]
res = ""
for i in PC_2:
res += my_key[i-1]
return res

密钥生成

1
2
3
4
5
6
7
8
9
10
11
12
def gen_key(key):
SHIFT = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
key_list = []
divide_output = change_key1(key)#密钥的PC-1置换
key_C0 = divide_output[0:28]#C0
key_D0 = divide_output[28:]#D0
for i in SHIFT:#根据移位位数规则循环左移
key_c = left_turn(key_C0,i)
key_d = left_turn(key_D0,i)
key_output = change_key2(key_c + key_d)#密钥的PC-2置换
key_list.append(key_output)
return key_list

循环左移

1
2
3
4
5
#循环左移操作
def left_turn(my_str,num):
left_res = my_str[num:len(my_str)]
left_res = my_str[0:num]+left_res
return left_res

初始置换和逆初始置换

初始置换(IP)是在第一轮迭代之前进行的,目的是将原明文块的位进行换位,其置换表是固定的。逆初始置换(IP-1)是初始置换的逆置换。

IP 最后一列为 2, 4, 6, 8, 1, 3, 5, 7,左一列比右一列+8 。

IP-1 第二列为 8, 7, 6, 5, 4, 3, 2, 1,右边第二列比当前列+8 。

初始置换

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
#IP盒处理
def ip_change(bin_str):
IP_table = [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]
res = ""
for i in IP_table:
res += bin_str[i-1] #数组下标i-1
return res

#IP逆盒处理
def ip_re_change(bin_str):
IP_re_table = [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]
res = ""
for i in IP_re_table:
res += bin_str[i-1]
return res

轮函数F

4

DES轮函数F由4部分组成

1
2
3
4
扩展置换(E盒)
密钥加
代换盒(S盒)
线性置换(P盒)

5

扩展置换

扩展置换又称E盒,它将32位输入扩展为48位输出。

3

扩展方法:每个输入分组的4位作为6位输出分组的中间4位,6位输出分组中的第1、6位分别由相邻两个4位分组的最外面两位扩散进入到本分组,其中第1个分组的左侧相邻分组为最后1个分组。

6

扩展置换的目的

1
2
3
E盒产生于子密钥相同长度的数据使得能进行异或运算
扩展后得数据在S盒得作用下能进行压缩,实现非线性变换
E盒输入的1位可能影响2个S盒的输入,所以输出对输入的依赖性将传播更快,从而快速实现雪崩效应
1
2
3
4
5
6
7
8
9
10
11
12
#E盒置换表
def e_key(bin_str):
E = [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]
res = ""
for i in E:
res += bin_str[i-1]
return res

密钥加

E扩展输出的48位数据与48位子密钥进行逐位异或运算,输出48位数据。

1
2
3
4
5
6
7
8
9
10
#字符串异或操作
def str_xor(my_str1,my_str2):
res = ""
for i in range(0,len(my_str1)):
xor_res = int(my_str1[i],10)^int(my_str2[i],10) #变成10进制是转化成字符串 2进制与10进制异或结果一样,都是1,0
if xor_res == 1:
res += '1'
else:
res += '0'
return res

这里变成10进制是转化成字符串,2进制与10进制异或结果一样,都是1,0。

代换盒

代换盒又称作S盒,其功能是进行非线性代换。

S盒是DES中唯一的非线性部分,经过S盒代换以后,E盒扩展生成的48位数据又重新被压缩成32位数据。

压缩替换S-盒由8个S-盒构成, 每个S-盒都是6比特的输入,4比特的输出。

7

​ S盒的构造

8

S盒设计准则

1
2
3
4
5
6
具有良好的非线性(输出的每个比特与全部输入比特有关)
每一行包括所有16种4位二进制
两个输入相差1比特时,输出相差2比特
如果两个输入刚好在中间两个比特上不同,则输出至少有两个比特不同
如果两个输入前两位不同而最后两位相同,则输出一定不同
相差6比特的输入共有32对,这32对中有不超过8对的输出相同
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
def s_box(my_str):
S = [
[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 ],

[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],

[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 ],

[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],

[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],

[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],

[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],

[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],
]
res = ""
c = 0
for i in range(0,len(my_str),6):
now_str = my_str[i:i+6]
row = int(now_str[0]+now_str[5],2)
col = int(now_str[1:5],2)
num = bin(S[c][row*16 + col])[2:] #利用了bin输出有可能不是4位str类型的值,所以才有下面的循环并且加上字符0
for j in range(0,4-len(num)):
num = '0'+ num
res += num
c += 1
return res

置换运算

置换运算(P盒)只是进行简单位置置换。

1
2
3
4
5
6
7
8
9
10
#P盒置换表
def p_box(bin_str):
P = [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]
res = ""
for i in P:
res += bin_str[i-1]
return res

F函数实现

1
2
3
4
5
6
def fun_f(bin_str,key):
first_output = e_key(bin_str)#E盒扩展置换为48位
second_output = str_xor(first_output,key)#key与扩展后的Ri-1异或
third_output = s_box(second_output)#S盒代换
last_output = p_box(third_output)#P盒置换
return last_output

加解密算法

加密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def des_encrypt_one(bin_message,bin_key): #64位二进制加密的测试
mes_ip_bin = ip_change(bin_message)#明文进行IP置换
key_lst = gen_key(bin_key)#获取加密密钥
mes_left = mes_ip_bin[0:32]#分成左右两部分,每部分32位
mes_right = mes_ip_bin[32:]
for i in range(0,15):#迭代15轮
mes_tmp = mes_right#存储不变的Ri
f_result = fun_f(mes_tmp,key_lst[i])#Ri与密钥进行F函数
mes_right = str_xor(f_result,mes_left)#Li与密钥进行异或
mes_left = mes_tmp#Li=Ri-1
#最后一轮
f_result = fun_f(mes_right,key_lst[15])
mes_fin_left = str_xor(mes_left,f_result)
mes_fin_right = mes_right
fin_message = ip_re_change(mes_fin_left + mes_fin_right)#R16与R15合并进行IP逆盒处理
return fin_message

解密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
##64位二进制解密的测试,注意密钥反过来了,不要写错了
def des_decrypt_one(bin_mess,bin_key):
mes_ip_bin = ip_change(bin_mess)
#bin_key = input_key_judge(str2bin(key))
key_lst = gen_key(bin_key)
lst = range(1,16)
cipher_left = mes_ip_bin[0:32]
cipher_right = mes_ip_bin[32:]
for i in lst[::-1]:
mes_tmp = cipher_right
cipher_right = str_xor(cipher_left,fun_f(cipher_right,key_lst[i]))
cipher_left = mes_tmp
fin_left = str_xor(cipher_left,fun_f(cipher_right,key_lst[0]))
fin_right = cipher_right
fin_output = fin_left + fin_right
bin_plain = ip_re_change(fin_output)
res = bin2str(bin_plain)
return res

填充密钥

全部密钥以补0的方式实现长度不满足64位的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#简单判断以及处理信息分组
def deal_mess(bin_mess):
ans = len(bin_mess)
if ans % 64 != 0:
for i in range( 64 - (ans%64)):
bin_mess += '0'
return bin_mess
#查看密钥是否为64位
def input_key_judge(bin_key):
ans = len(bin_key)
if len(bin_key) < 64:
if ans % 64 != 0:
for i in range(64 - (ans % 64)): # 不够64位补充0
bin_key += '0'
else:
bin_key = bin_key[0:64]
return bin_key

密钥超过64位的情况默认就是应该跟密文一样长直接将密钥变为跟明文一样的长度,虽然安全性会有所下降。

完整代码

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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# -*- coding: utf-8 -*-
import re

def encrypt_read_out_file():
try:
f = open('E:\Cryptology\DES\Plaintext.txt','r',encoding = 'utf-8')
mess = f.read()
f.close()
print("文件读取成功!")
return mess
except IOError:
print('文件加解密出错!!!')

def encrypt_write_in_file(str_mess):
try:
f = open('E:\Cryptology\DES\Plainouttext.txt','w',encoding='utf-8')
f.write(str_mess)
f.close()
print("文件输出成功!")
except IOError:
print('文件加解密出错!!!')

def decrypt_read_out_file():
try:
f = open('E:\Cryptology\DES\Ciphertext.txt','r',encoding = 'utf-8')
mess = f.read()
f.close()
print("文件读取成功!")
return mess
except IOError:
print('文件加解密出错!!!')

def decrypt_write_in_file(str_mess):
try:
f = open('E:\Cryptology\DES\Cipherouttext.txt','w',encoding='utf-8')
f.write(str_mess)
f.close()
print("文件输出成功!")
except IOError:
print('文件加解密出错!!!')

#字符串转化为二进制
def str2bin(message):
res = ""
for i in message:
tmp = bin(ord(i))[2:]
for j in range(0,8-len(tmp)):
tmp = '0'+ tmp
res += tmp
return res

#二进制转化为字符串
def bin2str(bin_str):
res = ""
tmp = re.findall(r'.{8}',bin_str)
for i in tmp:
res += chr(int(i,2))
return res

#IP盒处理
def ip_change(bin_str):
IP_table = [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]
res = ""
for i in IP_table:
res += bin_str[i-1] #数组下标i-1
return res

#IP逆盒处理
def ip_re_change(bin_str):
IP_re_table = [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]
res = ""
for i in IP_re_table:
res += bin_str[i-1]
return res

#E盒置换
def e_key(bin_str):
E = [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]
res = ""
for i in E:
res += bin_str[i-1]
return res


#字符串异或操作
def str_xor(my_str1,my_str2):
res = ""
for i in range(0,len(my_str1)):
xor_res = int(my_str1[i],10)^int(my_str2[i],10) #变成10进制是转化成字符串 2进制与10进制异或结果一样,都是1,0
if xor_res == 1:
res += '1'
else:
res += '0'
return res


#循环左移操作
def left_turn(my_str,num):
left_res = my_str[num:len(my_str)]
left_res = my_str[0:num]+left_res
return left_res


#密钥的PC-1置换
def change_key1(my_key):
PC_1 = [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]
res = ""
for i in PC_1:
res += my_key[i-1]
return res

#密钥的PC-2置换
def change_key2(my_key):
PC_2 = [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]
res = ""
for i in PC_2:
res += my_key[i-1]
return res


# S盒过程
def s_box(my_str):
S = [
[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 ],

[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],

[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 ],

[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],

[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],

[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],

[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],

[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],
]
res = ""
c = 0
for i in range(0,len(my_str),6):
now_str = my_str[i:i+6]
row = int(now_str[0]+now_str[5],2)
col = int(now_str[1:5],2)
num = bin(S[c][row*16 + col])[2:] #利用了bin输出有可能不是4位str类型的值,所以才有下面的循环并且加上字符0
for j in range(0,4-len(num)):
num = '0'+ num
res += num
c += 1
return res

#P盒置换
def p_box(bin_str):
P = [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]
res = ""
for i in P:
res += bin_str[i-1]
return res

# F函数的实现
def fun_f(bin_str,key):
first_output = e_key(bin_str)#E盒扩展置换为48位
second_output = str_xor(first_output,key)#key与扩展后的Ri-1异或
third_output = s_box(second_output)#S盒代换
last_output = p_box(third_output)#P盒置换
return last_output

#密钥生成
def gen_key(key):
SHIFT = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
key_list = []
divide_output = change_key1(key)#密钥的PC-1置换
key_C0 = divide_output[0:28]#C0
key_D0 = divide_output[28:]#D0
for i in SHIFT:#根据移位位数规则循环左移
key_c = left_turn(key_C0,i)
key_d = left_turn(key_D0,i)
key_output = change_key2(key_c + key_d)#密钥的PC-2置换
key_list.append(key_output)
return key_list

#64位二进制加密的测试
def des_encrypt_one(bin_message,bin_key):
mes_ip_bin = ip_change(bin_message)#明文进行IP置换
key_lst = gen_key(bin_key)#获取加密密钥
mes_left = mes_ip_bin[0:32]#分成左右两部分,每部分32位
mes_right = mes_ip_bin[32:]
for i in range(0,15):#迭代15轮
mes_tmp = mes_right#存储不变的Ri
f_result = fun_f(mes_tmp,key_lst[i])#Ri与密钥进行F函数
mes_right = str_xor(f_result,mes_left)#Li与密钥进行异或
mes_left = mes_tmp#Li=Ri-1
#最后一轮
f_result = fun_f(mes_right,key_lst[15])
mes_fin_left = str_xor(mes_left,f_result)
mes_fin_right = mes_right
fin_message = ip_re_change(mes_fin_left + mes_fin_right)#R16与R15合并进行IP逆盒处理
return fin_message

##64位二进制解密的测试,注意密钥反过来了,不要写错了
def des_decrypt_one(bin_mess,bin_key):
mes_ip_bin = ip_change(bin_mess)
#bin_key = input_key_judge(str2bin(key))
key_lst = gen_key(bin_key)
lst = range(1,16)
cipher_left = mes_ip_bin[0:32]
cipher_right = mes_ip_bin[32:]
for i in lst[::-1]:
mes_tmp = cipher_right
cipher_right = str_xor(cipher_left,fun_f(cipher_right,key_lst[i]))
cipher_left = mes_tmp
fin_left = str_xor(cipher_left,fun_f(cipher_right,key_lst[0]))
fin_right = cipher_right
fin_output = fin_left + fin_right
bin_plain = ip_re_change(fin_output)
res = bin2str(bin_plain)
return res

#简单判断以及处理信息分组
def deal_mess(bin_mess):
"""
:param bin_mess: 二进制的信息流
:return: 补充的64位信息流
"""
ans = len(bin_mess)
if ans % 64 != 0:
for i in range( 64 - (ans%64)): #不够64位补充0
bin_mess += '0'
return bin_mess

#查看密钥是否为64位
def input_key_judge(bin_key):
"""
全部密钥以补0的方式实现长度不满足64位的
:param bin_key:
"""
ans = len(bin_key)
if len(bin_key) < 64:
if ans % 64 != 0:
for i in range(64 - (ans % 64)): # 不够64位补充0
bin_key += '0'
else:
bin_key = bin_key[0:64] #密钥超过64位的情况默认就是应该跟密文一样长 直接将密钥变为跟明文一样的长度,虽然安全性会有所下降
return bin_key

def all_message_encrypt(message,key):
bin_mess = deal_mess(str2bin(message))
res = ""
bin_key = input_key_judge(str2bin(key))
tmp = re.findall(r'.{64}',bin_mess)
for i in tmp:
res += des_encrypt_one(i,bin_key)
return res

def all_message_decrypt(message,key):
bin_mess = deal_mess(str2bin(message))
res = ""
bin_key = input_key_judge(str2bin(key))
tmp = re.findall(r'.{64}',bin_mess)
for i in tmp:
res += des_decrypt_one(i,bin_key)
return res

def main():
print("1.加密")
print("2.解密")
mode = input()
if mode == '1':
message = encrypt_read_out_file()
key = "LGDISBEST"
s = all_message_encrypt(message,key)
out_mess = bin2str(s)
encrypt_write_in_file(out_mess)
elif mode == '2':
key = "LGDISBEST"
message = decrypt_read_out_file()
s = all_message_decrypt(message, key)
decrypt_write_in_file(s)
else:
print("请重新输入!")
main()
if __name__ == '__main__':
main()

安全性分析

  1. 互补性

    $$
    c=E_k(m)
    $$
    则有
    $$
    \overline{c}=E_{\overline{k}}(\overline{m})
    $$
    在选择明文攻击下所需的工作量减半,仅需要测试256个密钥的一半就可以破解。

  2. 弱密钥
    对于加密解密运算没有区别的密钥叫弱密钥。

    如果k为弱密钥,则对于任意的64位数据m,有
    $$
    E_k(E_k(m))=m
    $$

    $$
    D_k(D_k(m))=m
    $$
    有弱密钥产生是因为C、D存储的数据在循环移位时除位置外没有发生变化(C、D全为0或全为1)。

    此外还有半弱密钥,四分之一弱密钥和八分之一弱密钥,共256个。

    如果随机选取密钥,选中弱密钥的概率几乎可以忽略,但一般为了安全起见,在随机生成密钥后,要进行弱密钥检查,以保证不使用弱密钥作为DES的密钥。

  3. 迭代轮数
    对于低于16轮的DES已知明文攻击,差分分析攻击比穷举攻击有效。
    当DES进行到16轮迭代时,穷举攻击比差分分析攻击有效。

  4. 密钥长度
    DES密钥长度为56位,按照当时的计算能力,对于这个长度的密钥进行穷举攻击是不切实际的,但现在已经成为了现实。
    DES已经不足以保证敏感数据的安全,开始逐步退出历史舞台。
    人们针对DES设计了改进方法:多重DES。
    多重DES就是使用多个不同的DES密钥利用DES加密算法对明文进行多次加密。使用多重DES可以增加密钥量,从而大大提高抵抗对密钥的穷举搜索攻击的能力。

    1. 双重DES
      可以抵抗目前的穷举攻击,但无法抵抗中途相遇攻击,即获得一对明密文,使得一起从双方各自的起点出发,一段进行加密,另一端进行解密,当找到两端相同的时候,二重DES就被破解了。

      1

    2. 三重DES

      1
      2
      3
      4
      5
      密钥长度增加到112位或168位
      增强了抗差分分析和线性分析的能力
      更换成本小
      处理速度较慢
      明文分组长度没有变化

      因此三重DES知识在DES变得不安全的情况下的一种临时解决方案。