参考了这篇文章

简述

DES(Data Encryption Standard)是一种对称加密算法,它将64位的明文分成64位的块,并使用64位的密钥来进行加密操作。虽然DES本身只能处理64位的数据块,但是可以通过分组密码模式(如ECB、CBC、CFB、OFB、CTR等)来加密更长的数据。

初始置换

输入明文M(64位),根据下表进行初始置换,得到IP

1
2
3
4
5
6
7
8
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,

如将M的第58位填入IP的第一位。

然后将IP平分为两段,L0和R0

生成子密钥

一图以概之

4433f1aedb9f4df98c7cbb7a783a7bda

首先,一开始输入的64位key按照下表进行转换,生成56位密钥(置换选择1)

1
2
3
4
5
6
7
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

实际上,这里只从原始密钥中取了56位进行变换,原始密钥中每字节的最高位是没有被使用的。

得到子密钥(56位)后,将变换后的子密钥分成两部分,C0和D0

得到C0和D0后,分别进行循环左移位操作,得到C1C16和D1D16的值,每一次循环移位的位数如下:

1
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1

如C0=1111000011001100101010101111

C1=1110000110011001010101011111

C2=1100001100110010101010111111

将每组Cn和Dn组合起来,得到十六组数据再次按照下表进行替换,得到K1~K16(置换选择2)

1
2
3
4
5
6
7
8
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

轮函数

对上面初始置换中得到的L0和R0,我们在这里进行16轮加密变换,下面的图(嫖来的)描述了这个流程,其中函数f后面会叙述

2e1e54d0330a4d21a4aeada39bd0ca72

由图知,L0通过与f函数运算后的结果进行异或后成为了R1,R0直接用作L1

下面是对f函数实现过程的具体描述

01d6b86ae2844e53b304002dc3c36db2

扩充/置换:

此处的32位Rn先进行扩充置换变成了48位(因为我们需要将其与48位的子密钥异或)。

与密钥异或

在扩充后,与之前生成的对应轮数的子密钥Kn进行异或运算

代换/选择

将48位的数据转换为32位。这一步是整个DES算法的核心,简称S盒。

S盒

e6cccba809c648ad96d79f2119b1d083

对于每个S盒,对照着4行16列的表,6比特输入中的第1比特和第6比特组成的二进制数来确定行,中间4位二进制数来确定列,对应行、列位置的十进制数的4位二进制数表示作为输出。

置换

经过置换表进行置换

异或

与对应环节的Ln进行异或

交换

将L16和R16位置交换

逆初始置换

这里的置换方式与前面的不一样!

这里的方式与前面的都恰好相反。

例如:

原始数据的第1位置换到了第40位,第2位置换到了第8位。则逆置换就是将第40位置换到第1位,第8位置换到第2位。

1
2
3
4
5
6
7
8
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

至此可以得到密文C

代码实现

参考了这篇文章

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
import binascii

class ArrangeSimpleDES():
def __init__(self):
# 出初始化DES加密的参数
self.ip = [
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置换

self.ip1 = [
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,
] # 逆ip置换
self.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,
] # E置换,将32位明文置换位48位
self.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,
] # P置换,对经过S盒之后的数据再次进行置换
# 设置默认密钥
self.K = '0111010001101000011010010111001101101001011100110110100101110110'
self.k1 = [
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,
] # 密钥的K1初始置换
self.k2 = [
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,
]

self.k0 = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, ] # 秘钥循环移位的位数

self.S = [
[
0xe, 0x4, 0xd, 0x1, 0x2, 0xf, 0xb, 0x8, 0x3, 0xa, 0x6, 0xc, 0x5, 0x9, 0x0, 0x7,
0x0, 0xf, 0x7, 0x4, 0xe, 0x2, 0xd, 0x1, 0xa, 0x6, 0xc, 0xb, 0x9, 0x5, 0x3, 0x8,
0x4, 0x1, 0xe, 0x8, 0xd, 0x6, 0x2, 0xb, 0xf, 0xc, 0x9, 0x7, 0x3, 0xa, 0x5, 0x0,
0xf, 0xc, 0x8, 0x2, 0x4, 0x9, 0x1, 0x7, 0x5, 0xb, 0x3, 0xe, 0xa, 0x0, 0x6, 0xd,
],
[
0xf, 0x1, 0x8, 0xe, 0x6, 0xb, 0x3, 0x4, 0x9, 0x7, 0x2, 0xd, 0xc, 0x0, 0x5, 0xa,
0x3, 0xd, 0x4, 0x7, 0xf, 0x2, 0x8, 0xe, 0xc, 0x0, 0x1, 0xa, 0x6, 0x9, 0xb, 0x5,
0x0, 0xe, 0x7, 0xb, 0xa, 0x4, 0xd, 0x1, 0x5, 0x8, 0xc, 0x6, 0x9, 0x3, 0x2, 0xf,
0xd, 0x8, 0xa, 0x1, 0x3, 0xf, 0x4, 0x2, 0xb, 0x6, 0x7, 0xc, 0x0, 0x5, 0xe, 0x9,
],
[
0xa, 0x0, 0x9, 0xe, 0x6, 0x3, 0xf, 0x5, 0x1, 0xd, 0xc, 0x7, 0xb, 0x4, 0x2, 0x8,
0xd, 0x7, 0x0, 0x9, 0x3, 0x4, 0x6, 0xa, 0x2, 0x8, 0x5, 0xe, 0xc, 0xb, 0xf, 0x1,
0xd, 0x6, 0x4, 0x9, 0x8, 0xf, 0x3, 0x0, 0xb, 0x1, 0x2, 0xc, 0x5, 0xa, 0xe, 0x7,
0x1, 0xa, 0xd, 0x0, 0x6, 0x9, 0x8, 0x7, 0x4, 0xf, 0xe, 0x3, 0xb, 0x5, 0x2, 0xc,
],
[
0x7, 0xd, 0xe, 0x3, 0x0, 0x6, 0x9, 0xa, 0x1, 0x2, 0x8, 0x5, 0xb, 0xc, 0x4, 0xf,
0xd, 0x8, 0xb, 0x5, 0x6, 0xf, 0x0, 0x3, 0x4, 0x7, 0x2, 0xc, 0x1, 0xa, 0xe, 0x9,
0xa, 0x6, 0x9, 0x0, 0xc, 0xb, 0x7, 0xd, 0xf, 0x1, 0x3, 0xe, 0x5, 0x2, 0x8, 0x4,
0x3, 0xf, 0x0, 0x6, 0xa, 0x1, 0xd, 0x8, 0x9, 0x4, 0x5, 0xb, 0xc, 0x7, 0x2, 0xe,
],
[
0x2, 0xc, 0x4, 0x1, 0x7, 0xa, 0xb, 0x6, 0x8, 0x5, 0x3, 0xf, 0xd, 0x0, 0xe, 0x9,
0xe, 0xb, 0x2, 0xc, 0x4, 0x7, 0xd, 0x1, 0x5, 0x0, 0xf, 0xa, 0x3, 0x9, 0x8, 0x6,
0x4, 0x2, 0x1, 0xb, 0xa, 0xd, 0x7, 0x8, 0xf, 0x9, 0xc, 0x5, 0x6, 0x3, 0x0, 0xe,
0xb, 0x8, 0xc, 0x7, 0x1, 0xe, 0x2, 0xd, 0x6, 0xf, 0x0, 0x9, 0xa, 0x4, 0x5, 0x3,
],
[
0xc, 0x1, 0xa, 0xf, 0x9, 0x2, 0x6, 0x8, 0x0, 0xd, 0x3, 0x4, 0xe, 0x7, 0x5, 0xb,
0xa, 0xf, 0x4, 0x2, 0x7, 0xc, 0x9, 0x5, 0x6, 0x1, 0xd, 0xe, 0x0, 0xb, 0x3, 0x8,
0x9, 0xe, 0xf, 0x5, 0x2, 0x8, 0xc, 0x3, 0x7, 0x0, 0x4, 0xa, 0x1, 0xd, 0xb, 0x6,
0x4, 0x3, 0x2, 0xc, 0x9, 0x5, 0xf, 0xa, 0xb, 0xe, 0x1, 0x7, 0x6, 0x0, 0x8, 0xd,
],
[
0x4, 0xb, 0x2, 0xe, 0xf, 0x0, 0x8, 0xd, 0x3, 0xc, 0x9, 0x7, 0x5, 0xa, 0x6, 0x1,
0xd, 0x0, 0xb, 0x7, 0x4, 0x9, 0x1, 0xa, 0xe, 0x3, 0x5, 0xc, 0x2, 0xf, 0x8, 0x6,
0x1, 0x4, 0xb, 0xd, 0xc, 0x3, 0x7, 0xe, 0xa, 0xf, 0x6, 0x8, 0x0, 0x5, 0x9, 0x2,
0x6, 0xb, 0xd, 0x8, 0x1, 0x4, 0xa, 0x7, 0x9, 0x5, 0x0, 0xf, 0xe, 0x2, 0x3, 0xc,
],
[
0xd, 0x2, 0x8, 0x4, 0x6, 0xf, 0xb, 0x1, 0xa, 0x9, 0x3, 0xe, 0x5, 0x0, 0xc, 0x7,
0x1, 0xf, 0xd, 0x8, 0xa, 0x3, 0x7, 0x4, 0xc, 0x5, 0x6, 0xb, 0x0, 0xe, 0x9, 0x2,
0x7, 0xb, 0x4, 0x1, 0x9, 0xc, 0xe, 0x2, 0x0, 0x6, 0xa, 0xd, 0xf, 0x3, 0x5, 0x8,
0x2, 0x1, 0xe, 0x7, 0x4, 0xa, 0x8, 0xd, 0xf, 0xc, 0x9, 0x0, 0x3, 0x5, 0x6, 0xb,
],
] # 16进制表示S盒的数据,S盒是为了将48位转换为32位,有8个盒子

def __substitution(self, table: str, self_table: list) -> str:
"""
:param table: 需要进行置换的列表,是一个01字符串
:param self_table: 置换表,在__init__中初始化了
:return: 返回置换后的01字符串
"""
sub_result = ""
for i in self_table:
sub_result += table[i - 1]
return sub_result

def str2bin(self, string: str) -> str:
"""
将明文转为二进制字符串:
:param string: 任意字符串
:return:二进制字符串
"""
plaintext_list = list(bytes(string, 'utf8')) # 将字符串转成bytes类型,再转成list
result = [] # 定义返回结果
for num in plaintext_list:
result.append(bin(num)[2:].zfill(8)) # 将列表的每个元素转成二进制字符串,8位宽度
return "".join(result)

def bin2str(self, binary: str) -> str:
"""
二进制字符串转成字符串
:param binary:
:return:
"""
list_bin = [binary[i:i + 8] for i in range(0, len(binary), 8)] # 对二进制字符串进行切分,每8位为一组
list_int = []
for b in list_bin:
list_int.append(int(b, 2)) # 对二进制转成int
result = bytes(list_int).decode() # 将列表转成bytes,在进行解码,得到字符串
return result

def __bin2int(self, binary: str) -> list:
"""
由于加密之后的二进制无法直接转成字符,有不可见字符在,utf8可能无法解码,所以需要将二进制字符串每8位转成int型号列表,用于转成bytes再转hex
:param binary: 二进制字符串
:return: int型列表
"""
list_bin = [binary[i:i + 8] for i in range(0, len(binary), 8)] # 对二进制字符串进行切分,每8位为一组
list_int = []
for b in list_bin:
list_int.append(int(b, 2))
return list_int

def __int2bin(self, list_int: list) -> str:
result = []
for num in list_int:
result.append(bin(num)[2:].zfill(8))
return ''.join(result)

def __get_block_list(self, binary: str) -> list:
"""
对明文二进制串进行切分,每64位为一块,DES加密以64位为一组进行加密的
:type binary: 二进制串
"""
len_binary = len(binary)
if len_binary % 64 != 0:
binary_block = binary + ("0" * (64 - (len_binary % 64)))
return [binary_block[i:i + 64] for i in range(0, len(binary_block), 64)]
else:
return [binary[j:j + 64] for j in range(0, len(binary), 64)]

def modify_secretkey(self):
"""
修改默认密钥函数
:return: None
"""
print('当前二进制形式密钥为:{}'.format(self.K))
print("当前字符串形式密钥为:{}".format(self.bin2str(self.K)))
newkey = input("输入新的密钥(长度为8):")
if len(newkey) != 8:
print("密钥长度不符合,请重新输入:")
self.modify_secretkey()
else:
bin_key = self.str2bin(newkey)
self.K = bin_key
print("当前二进制形式密钥为:{}".format(self.K))

def __f_funtion(self, right: str, key: str):
"""
:param right: 明文二进制的字符串加密过程的右半段
:param key: 当前轮数的密钥
:return: 进行E扩展,与key异或操作,S盒操作后返回32位01字符串
"""
# 对right进行E扩展
e_result = self.__substitution(right, self.E)
# 与key 进行异或操作
xor_result = self.__xor_function(e_result, key)
# 进入S盒子
s_result = self.__s_box(xor_result)
# 进行P置换
p_result = self.__substitution(s_result, self.P)
return p_result

def __get_key_list(self):
"""
:return: 返回加密过程中16轮的子密钥
"""
key = self.__substitution(self.K, self.k1)
left_key = key[0:28]
right_key = key[28:56]
keys = []
for i in range(1, 17):
move = self.k0[i - 1]
move_left = left_key[move:28] + left_key[0:move]
move_right = right_key[move:28] + right_key[0:move]
left_key = move_left
right_key = move_right
move_key = left_key + right_key
ki = self.__substitution(move_key, self.k2)
keys.append(ki)
return keys

def __xor_function(self, xor1: str, xor2: str):
"""
:param xor1: 01字符串
:param xor2: 01字符串
:return: 异或操作返回的结果
"""
size = len(xor1)
result = ""
for i in range(0, size):
result += '0' if xor1[i] == xor2[i] else '1'
return result

def __s_box(self, xor_result: str):
"""
:param xor_result: 48位01字符串
:return: 返回32位01字符串
"""
result = ""
for i in range(0, 8):
# 将48位数据分为6组,循环进行
block = xor_result[i * 6:(i + 1) * 6]
line = int(block[0] + block[5], 2)
colmn = int(block[1:4], 2)
res = bin(self.S[i][line*16 + colmn])[2:]
if len(res) < 4:
res = '0' * (4 - len(res)) + res
result += res
return result

def __iteration(self, bin_plaintext: str, key_list: list):
"""
:param bin_plaintext: 01字符串,64位
:param key_list: 密钥列表,共16个
:return: 进行F函数以及和left异或操作之后的字符串
"""
left = bin_plaintext[0:32]
right = bin_plaintext[32:64]
for i in range(0, 16):
next_lift = right
f_result = self.__f_funtion(right, key_list[i])
next_right = self.__xor_function(left, f_result)
left = next_lift
right = next_right
bin_plaintext_result = left + right
return bin_plaintext_result[32:] + bin_plaintext_result[:32]

def encode(self, plaintext):
"""
:param plaintext: 明文字符串
:return: 密文字符串
"""
bin_plaintext = self.str2bin(plaintext)
bin_plaintext_block = self.__get_block_list(bin_plaintext)
ciphertext_bin_list = []
key_list = self.__get_key_list()
for block in bin_plaintext_block:
# 初代ip置换
sub_ip = self.__substitution(block, self.ip)
ite_result = self.__iteration(sub_ip, key_list)
# 逆ip置换
sub_ip1 = self.__substitution(ite_result, self.ip1)
ciphertext_bin_list.append(sub_ip1)
ciphertext_bin = ''.join(ciphertext_bin_list)
result = self.__bin2int(ciphertext_bin)
return bytes(result).hex().upper()

def decode(self, ciphertext):
'''
:param ciphertext: 密文字符串
:return: 明文字符串
'''
b_ciphertext = binascii.a2b_hex(ciphertext)
bin_ciphertext = self.__int2bin(list(b_ciphertext))
bin_plaintext_list = []
key_list = self.__get_key_list()
key_list = key_list[::-1]
bin_ciphertext_block = [bin_ciphertext[i:i + 64] for i in range(0, len(bin_ciphertext), 64)]
for block in bin_ciphertext_block:
sub_ip = self.__substitution(block, self.ip)
ite = self.__iteration(sub_ip, key_list)
sub_ip1 = self.__substitution(ite, self.ip1)
bin_plaintext_list.append(sub_ip1)
bin_plaintext = ''.join(bin_plaintext_list).replace('00000000', '')
return self.bin2str(bin_plaintext)

def main(self):
select = input("Please selecting:\n1、Encryption\t 2、Decrpytion\nYour selecting:")
if select == '1':
plaintext = input("Input plaintext:")
# print("Your plaintext is:{}".format(plaintext))
ciphertext = self.encode(plaintext)
print("The ciphertext is:{}".format(ciphertext))
elif select == '2':
plaintext = input("Input ciphertext:")
# print("Your ciphertext is:{}".format(plaintext))
plaintext = self.decode(plaintext)
print("The plaintext is:{}".format(plaintext))
# print(len(plaintext))
else:
input("Please selecting again!")
self.main()


if __name__ == '__main__':
mydes = ArrangeSimpleDES()
mydes.modify_secretkey()
while True:
mydes.main()
print("")

另外要说的是,python也有模块可以直接进行des的加解密,但是真要考DES多半不会直接用这个就能出。。。

1
2
3
4
5
6
7
8
from Crypto.Cipher import DES
message = b''
key = b''
cipher = DES.new(key, DES.MODE_ECB) #这里使用了ECB模式
ciphertext = cipher.encrypt(message) #加密
print(ciphertext)
plain = cipher.decrypt(ciphertext) #解密
print(plain)

3DES

由于DES的实际密钥长度只有56位,安全性并不高,于是便有了3DES,可以说是对明文进行了3次DES,因此密钥有168位。

流程如下(图来源这篇文章

DES-EDE3

三个密钥都不同时

2019011314264658

DES-EDE2

当密钥1、3相同,密钥2不同时

20190113142411607

当三个密钥都相同时,就成了普通的DES

关于DES的攻击

这里有待补充