密码学实验课程设计

一、古典密码

仿射密码

简介

仿射加密在本质上还是一个置换密码:如果说移位密码是一个常数级别的置换,那么,仿射加密是一个一次级别的置换

加密原理

假设X,Y,A,B是Z26整数环中的元素,A和B为密钥,X是原文,Y是密文

加密函数:Y=(AX+B)%26

解密函数:Y=(AX+B)%26,得到:X=(A的逆元)*(Y-B)%26

原理很简单,代码实现也比较简单

代码实现

各个函数作用

1
2
3
4
gcd(a,b)a,b的最大公因子
findModReverse(a,m) 计算a模m的逆元
encode(a,b,s) 仿射加密
decode(a,b,s) 仿射解密

利用扩展的欧几里得算法求逆元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def gcd(a,b):  # 求出最大公因数
while a!=0:
a,b = b%a,a
return b

def findModReverse(a,m): #扩展欧几里得算法求模逆
if gcd(a,m)!=1:
return None
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3//v3
v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
return u1%m

完整代码如下

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
def gcd(a,b):  # 求出最大公因数
while a!=0:
a,b = b%a,a
return b

def findModReverse(a,m): #扩展欧几里得算法求模逆
if gcd(a,m)!=1:
return None
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3//v3
v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
return u1%m

def encode(a,b,s):
result = "" # 保存加密结果
s = s.upper() # 全部转换为大写,方便计算
for i in range(0,len(s)):
s2 = chr((a*(ord(s[i])-65)+b)%26 + 65) #使用加密公式 Y=(AX+B)%26
result = result +s2
print(result.lower())

def decode(a,b,s):
a1 = findModReverse(a,26)
result = ""
s = s.upper()
for i in range(0, len(s)):
s2 = chr((a1 * (ord(s[i]) - 65 - b)) % 26 + 65) # 使用解密公式X=(A的逆元)*(Y-B)%26
result = result + s2
print(result.lower())

def s_decode(a,b,s):
letter ='abcdefghijklmnopqrstuvwxyz'
letter = letter.upper()
s = s.upper()
result = ""
for i in s:
for j in range(0,len(letter)):
if i == letter[(a*j+b)%26]:
result = result+letter[j]
print(result.lower())

def main(): # 主函数入口
answer = input(f'请输入所需的操作:编码/E or 解码/D: ')
try:
if answer.upper() == 'E':
a = int(input('请输入a:'))
b = int(input('请输入b:'))
s = input('请输入需要加密的字符:')
encode(a, b, s)
elif answer.upper() == 'D':
a = int(input('请输入a:'))
b = int(input('请输入b:'))
s = input('请输入需要解密的字符:')
decode(a, b, s) # 利用逆元解密
# s_decode(a,b,s) # 暴力枚举每一个字符
else:
print('输入错误!')
except KeyError:
print('请勿输入空格!')

if __name__ == '__main__':
main()
# a = 7, b = 22
# plain = firstthesentenceandthentheevidencesaidthequeen
# crypto = falszztysyjzyjkywjrztyjztyynaryjkyswarztyegyyj

加解密结果如图所示

image-20201223225306141

image-20201223225317161

破解仿射密码

这里使用了加解密中的 gcd findModReverse,找出26以内且与26互素的所有数的逆元,并存放在一个列表中

1
2
3
4
5
6
7
8
def findAllre():    # 找出所有小于26且与26互素的数
re_all = []
for i in range(1,26):
if gcd(i,26) == 1:
res = findModReverse(i,26)
re_all.append(res)
#re_all.sort()
return re_all

完整代码如下

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
def gcd(a,b):  # 求出最大公因数
while a!=0:
a,b = b%a,a
return b

def findModReverse(a,m): #扩展欧几里得算法求模逆
if gcd(a,m)!=1:
return None
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3//v3
v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
return u1%m

def findAllre(): # 找出所有小于26且与26互素的数
re_all = []
for i in range(1,26):
if gcd(i,26) == 1:
res = findModReverse(i,26)
re_all.append(res)
#re_all.sort()
return re_all

def decode(s):
re_all = findAllre()
for k1 in re_all:
for k2 in range(0, 26):
result = ""
for i in range(len(s)):
s2 = chr(((int(k1) * (ord(s[i]) - 97 - k2)) % 26 + 97))
result = result + s2
print("k1=" + str(findModReverse(k1,26)) + ", k2=" + str(k2) + " plaintext = " + result)

def main():
# criphertext = 'falszztysyjzyjkywjrztyjztyynaryjkyswarztyegyyj'
criphertext = input("请输入要破解的密文: ")
criphertext = criphertext.lower()
print("---------"*3+"strat attck"+"---------"*3)
decode(criphertext)

if __name__ == '__main__':
main()

效果如下

image-20201223170037595

维吉尼亚密码

1.简介

维吉尼亚密码(又译维热纳尔密码)是使用一系列凯撒密码组成密码字母表的加密算法,属于多表密码的一种简单形式。

维吉尼亚密码曾多次被发明。该方法最早记录在吉奥万·巴蒂斯塔·贝拉索( Giovan Battista Bellaso)于1553年所著的书《吉奥万·巴蒂斯塔·贝拉索先生的密码》(意大利语:La cifra del. Sig. Giovan Battista Bellaso)中。然而,后来在19世纪时被误传为是法国外交官布莱斯·德·维吉尼亚(Blaise De Vigenère)所创造,因此现在被称为“维吉尼亚密码”。

维吉尼亚密码以其简单易用而著称,同时初学者通常难以破解,因而又被称为“不可破译的密码”。这也让很多人使用维吉尼亚密码来加密的目的就是为了将其破解。

2.加解密原理

image-20201223225441528

加解密代码

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
# 将密钥处理成和密文/明文一样长
def solve_key(s,key):
nkey = key
while len(nkey) < len(s):
nkey = nkey+key
nkey = nkey[:len(s)]
return nkey

# 加密函数
def encode(s,key):
print('加密后的结果: ',end='')
s1 = s.upper()
key1 = solve_key(s, key)
key1 = key1.upper()

result = ""
for i in range(0,len(s)):
s2 = chr(abs(((ord(s1[i])-65)+(ord(key1[i])-65)) % 26) + 65)
result = result + s2
print(result.lower())

# 解密函数
def decode(s,key):
print('解密后的结果: ', end='')
s1 = s.upper()
key1 = solve_key(s, key)
key1 = key1.upper()

result = ""
for i in range(0, len(s)):
s2 = chr(((ord(s1[i]) - 65) - (ord(key1[i]) - 65)) % 26 + 65)
result = result + s2
print(result.lower())

def main():
while 1:
# 函数入口
answer = input(f'请输入所需的操作:编码/E or 解码/D: ')
try:
if answer.upper() == 'E':
key = input('请输入密钥: ')
key = "".join(filter(str.isalpha, key))
s = input('请输入明文: ')
s = "".join(filter(str.isalpha, s)) # 将字符串中的非字母字符去掉
# print(s)
encode(s, key)
elif answer.upper() == 'D':
key = input('请输入密钥: ')
key = "".join(filter(str.isalpha, key))
s = input('请输入密文: ')
s = "".join(filter(str.isalpha, s))
decode(s, key)
else:
print('输入错误!')
except KeyError:
print('请检查输入是否正确!')

if __name__ == '__main__':
main()

二、序列密码

LFSR

代码实现

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
def lsrf(inti, top):
sum = 0
inti2 = "0"*len(inti)
inti2 = list(inti2)
inti1 = ''
for i in range(len(inti)):
if top[i] == "1":
sum += int(inti[i])
sum = sum % 2
for i in range(len(inti)):
if i == 0:
inti2[i] = str(sum)
else:
inti2[i] = inti[i - 1]
inti1 = inti1.join(inti2)
return inti1

def main():
inti_str = str(input("请输入初始化序列:"))
inti_str = inti_str[::-1]
inti_str_backup = inti_str
top = str(input("请输入本原多项式:"))
top = top[::-1]
for i in range(2 ** len(inti_str) + 1):
if inti_str_backup == inti_str and i != 0 and i == 2 ** len(inti_str) - 1:
print("第{0}次".format(i), inti_str_backup)
print("是本原多项式且周期是" + str(i))
break
elif inti_str_backup == inti_str and i != 0 and i != 2 ** len(inti_str) - 1:
print("第{0}次".format(i), inti_str_backup)
print("不是本原多项式且周期是" + str(i))
break
print("第{0}次".format(i), inti_str_backup)
inti_str_backup = lsrf(inti_str_backup, top)

if __name__ == '__main__':
main()

选择本原多项式和初始序列如下

1
2
100000000001000000
011100010100100101

程序运行结果

image-20201223222649800

RC4

完整代码

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
import hashlib
import base64

# S盒初始化置换,Key为密钥
def Rc4_init(S, Key):
j = 0
Key = Key.encode('UTF-8')
Key = hashlib.md5(Key).hexdigest() # 长度为32的字符串
tmp = []
for i in range(256):
S.append(i)
tmp.append(Key[i % len(Key)])
for i in range(256):
j = (j + S[i] + ord(tmp[i])) % 256
S[i], S[j] = S[j], S[i] # 交换S[i],S[j]

def rc4_Encode(S, plaintext):
i = j = 0
result = ''
for a in plaintext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
k = chr(ord(a) ^ S[t])
result += k
result = base64.b64encode(result.encode('UTF-8'))
result = result.decode()
return result


def rc4_Decode(S, criphtext):
i = j = 0
criphtext = base64.b64decode(criphtext)
criphtext = str(criphtext.decode())
result = ''
for a in criphtext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
k = chr(ord(a) ^ S[t])
result += k
return result

def main():
while 1:
order = input("请输入指令,加密/E,解密/D :")
if order.upper() =='E':
plaintext = input('请输入明文: ')
key = input("请输入密钥: ")
s = []
Rc4_init(s, key)
cryphtext = rc4_Encode(s, plaintext)
print("密文为: ", cryphtext)
print('\n')

else:
cryphtext = input("请输入密文: ")
key = input("请输入密钥: ")
s = []
Rc4_init(s, key)
plaintext = rc4_Decode(s, cryphtext)
print("明文为: ", plaintext)
print('\n')
if __name__ == '__main__':
main()

三、DES

DEC_ECB模式

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
# 两字符进行异或运算
def xor(str1, str2):
res = ""
for i in range(0, len(str1)):
xor_res = int(str1[i], 10)^int(str2[i], 10)
if xor_res == 1:
res += '1'
else:
res += '0'
return res

# 处理字符串,将每个字符串都转成八位二进制数
def str_process(str):
res = ""
for i in str:
tmp = bin(ord(i))[2:]
tmp = (8 - len(tmp)) * '0' + tmp # 不够八位则在前面补 0
res += tmp
return res

# PC-1盒处理
def key_change_1(str):
change_table = [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 change_table:
res += str[i-1]
return res

# PC-2盒处理
def key_change_2(str):
change_table = [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 change_table:
res += str[i-1]
return res


# 循环左移
def left_run(str, num):
tmp_str = str[num:len(str)]
tmp_str = tmp_str+str[0:num]
return tmp_str


# 生成16个子密钥
def key_gen(str):
key_list = []
key_change_res = key_change_1(str)
key_c = key_change_res[0:28]
key_d = key_change_res[28:]
num = [0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
for i in range(1, 17): #共16轮
key_c = left_run(key_c, num[i])
key_d = left_run(key_d, num[i])
key_yiwei = key_c + key_d
key_res = key_change_2(key_yiwei)
key_list.append(key_res)
return key_list

# IP盒处理
def begin_change(str):
change_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 change_table:
res += str[i-1]
return res


# E盒处理 32位->48位
def E_box(str):
change_table = [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 change_table:
res += str[i-1]
return res

# s盒处理 48位->32位
def S_box(str):
j = 0
s_list = [[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 = ""
for i in range(0, len(str), 6):
begin_s = str[i:i + 6]
row = int(begin_s[0] + begin_s[5], 2) #第一位和第六位作为行
col = int(begin_s[1:5], 2) #中间四位作为列
index = s_list[j][row * 16 + col]
num = bin(index)[2:] #将匹配的数字转换位二进制数
for k in range(0, 4 - len(num)): #不够4位则补0
num = "0" + num
res += num
j = j + 1
return res

# p盒处理 置换操作
def P_box(str):
res = ""
change_table = [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]
for i in change_table:
res += str[i - 1]
return res

# F函数
def F_function(str, key): # R(32位)->E盒(48位)->与key的子密钥异或->S盒->P置换
str_e_res = E_box(str) # 将 E 异或 S P 集合到一个函数种,便于调用
xor_res = xor(str_e_res, key)
str_s_res = S_box(xor_res)
str_p_res = P_box(str_s_res)
return str_p_res

# 逆IP盒
def IP_re(str):
res = ""
ip_list = [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 ]
for i in ip_list:
res += str[i-1]
return res


# DES加密操作
def DESencode(text, key):
text_bin = str_process(text) # 将字符转换为二进制数
text_IP = begin_change(text_bin) # 明文初始置换
key_bin = str_process(key) # 将密钥转换位二进制数
key_list = key_gen(key_bin) # key_lsst 数组中存放着十六个子密钥

text_left = text_IP[0:32] # R0
text_right = text_IP[32:] # L0

for i in range(0, 15): # 十五轮加密

mes_tmp = text_right # 临时变量用于左右两部分交换
text_right = xor(F_function(text_right, key_list[i]) , text_left) #F 函数的作用 R(32位)->E盒(48位)->与key的子密钥异或(32位)->S盒(32位)->P置换(32位)
text_left = mes_tmp
fin_right = text_right # 第十六轮加密
fin_left = xor(F_function(text_right, key_list[15]), text_left)
criph_text = fin_left + fin_right
criph_text = IP_re(criph_text) # IP逆置换
return criph_text

# 针对一组的解密程序
def DESdecode(text, key): #密文直接输64位2进制
key_bin = str_process(key) # 将密钥转换为二进制数
key_list = key_gen(key_bin) # 生成的十六个子密钥
text = begin_change(text) # 先初始值换 与加密过程相反
cipher_left = text[0:32] # R16
cipher_right = text[32:] # L16
i = 15
while i > 0: # 十五轮加密 反过来
cipher_tmp = cipher_right #设置一个临时变量用于后面的交换
cipher_right = xor(cipher_left, F_function(cipher_right, key_list[i])) # F 函数的作用 R(32位)->E盒(48位)->与key的子密钥异或(32位)->S盒(32位)->P置换(32位)
# F 函数处理完后与L(32位)异或
cipher_left = cipher_tmp # 左右交换完成
i = i - 1
left_text = xor(cipher_left, F_function(cipher_right, key_list[0])) # 一
right_text = cipher_right # 二 三 这三步是第十六轮加密
plain_bin = left_text + right_text #
plain_bin = IP_re(plain_bin) #
plain_text = ""
for i in range(0, len(plain_bin), 8):
plain_text += chr(int(plain_bin[i:i + 8], 2))
return plain_text


def Divide_text(order,text,key): # 将明文或者明文分组 明文分成8个字符一组,密文则分成64bit一组
block_text = []
res = ""
length = 0
if order == "E":
length = 8
else:
length = 64
i = 0
while text[i:i+length] != "":
block_text.append(text[i:i+length])
i += length

if order == 'E':
if len(block_text[-1]) != 8: # 最后一组明文如果不够八个字符则添加 + 补齐八个 否则程序会报错
block_text[-1] = block_text[-1] + '+' * (8 - len(block_text[-1]))
for text in block_text: # 分别对每组加密
res += DESencode(text, key)
else:
for text in block_text: # 对密文解密
res += DESdecode(text, key)
return res

def main():
while 1:
plaintext = ''
ciphertext = ''
key = ''
order = input("加密请按E,解密请按D:")
if order == 'E':
plaintext = input("请输入明文:")
key = input("请输入密钥:")
ciphertext = Divide_text(order, plaintext, key)
print("密文是:")
print(ciphertext)
else:
ciphertext = input("请输入密文:")
key = input("请输入密钥:")
plaintext = Divide_text(order, ciphertext, key)
print("明文是:")
print(plaintext)

if __name__ == '__main__':
main()

DES_CBC模式

只需要修改ECB模式中的 Divide_text 部分

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
def Divide_text(order,text,key):    # 将明文或者明文分组 明文分成8个字符一组,密文则分成64bit一组
block_text = []
IV = 'aaaaaaaa' # 初始化向量
IV_bin = str_process(IV)
res = ""
length = 0
if order == "E":
length = 8
else:
length = 64
i = 0
while text[i:i+length] != "":
block_text.append(text[i:i+length])
i += length
tmp = ""
# 加密
if order == 'E':
if len(block_text[-1]) != 8: # 最后一组明文如果不够八个字符则添加 + 补齐八个 否则程序会报错
block_text[-1] = block_text[-1] + '+' * (8 - len(block_text[-1]))

for i in range(len(block_text)): # 分别对每组加密
if i == 0 :
tmp = DESencode(block_text[i],key, IV_bin)
res += tmp
else:
tmp = DESencode(block_text[i],key,tmp)
res += tmp
# 解密
else:
for i in range(len(block_text)):
if i == 0:
tmp = block_text[i]
res += DESdecode(block_text[i], key, IV_bin)
else:
res += DESdecode(block_text[i], key, tmp)
tmp = block_text[i]
return res

四、RSA

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
# -*- coding = utf - 8 -*-
#@Time : 2020/12/23 14:24
#@Author : sunzy
#@File : rsa.py

from Crypto.Util.number import *
import random

# 模重复平方法
def fast_mod(p,q,n): # p为底数,p为指数
res = 1
while q:
if q & 1:
res = (res * p) % n
q >>= 1 # 右移1位
p = (p * p) % n
return res

# 计算出d
#这个扩展欧几里得算法求模逆,用于求d
def caculateD(a, m):
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3//v3
v1,v2,v3,u1,u2,u3 = (u1-q*v1),(u2-q*v2),(u3-q*v3),v1,v2,v3
return u1%m


# 将字符转化为十六进制字符串
def str2Hex(m):
return "".join("{:02x}".format(ord(x)) for x in m)

# 素性检验:采用 Miler-Rabin 检验法
# 所有的𝑟 ∈ [0, 𝑠 − 1],若𝑎^𝑑 ≠ 1(𝑚𝑜𝑑 𝑛)且𝑎^((2^𝑟)*𝑑) ≠ −1(𝑚𝑜𝑑 𝑛),则𝑛是合数。否则,𝑛有 3/4的概率为素数
def miller_rabin(n):
s = n - 1
t = 0
while s % 2 == 0: # n,s,t之间的关系为 n = 2^s * t
s = s // 2
t += 1
for trials in range(10): # 可以多增加几轮保证大概率为素数
a = random.randrange(2, n - 1) # 随机生成a
v = pow(a, s, n) # 验证 a^(n-1) mod n
if v != 1:
i = 0
while v != (n - 1):
if i == t - 1:
return False
else:
i = i + 1
v = (v ** 2) % n
return True
# 生成素数 先生成1024位的奇数,再进行素性检验,通过则生成该素数
def genPrime(b=1024):
while True: # 设置死循环直到生成素数才退出
res = "1"
for i in range(b-2):
res += str(random.randint(0,1))
res += "1" # 最后一位为1保证为奇数
res = int(res,2)
if miller_rabin(res):
return res # 直到该数通过素数检验才推出循环

def genE(phi_n):
while True:
e = genPrime(b=random.randint(3,13)) #随机生成e
if e < 2000 : # e不能太小
continue
if phi_n%e != 0: # 保证e不能被phi整除
return e

def RSAEncode(m, e, n): # 加密公式 m^e mod n
m = int(str2Hex(m), 16) # 将字符转换为二进制
c = fast_mod(m, e, n)
return c

def RSADecode(c, d, n): # 加密公式 c^d mod n
plaintext = fast_mod(c,d,n)
plaintext = str(long_to_bytes(plaintext).decode()) # 将数字转换为字符
return plaintext


def main():
# 生成两个大素数p和q
print("Generate p,q and e, please wait... ")
p = genPrime()
q = genPrime()
print ("p = "+str(p))
print ("q = "+str(q))
n = p*q
print ("n = "+str(n))
# 用欧拉定理计算 phi_n
phi_n = (p-1)*(q-1)
# 生成e
e = genE(phi_n)
print ("e = "+str(e))
# m = "Hello world!"
m = str(input('请输入明文: '))
# 加密算法
Cryphtext = RSAEncode(m, e, n)
print ("The Ciphertext is: "+str(Cryphtext))
# 解密算法
d = caculateD(e, phi_n)
Plaintext = RSADecode(Cryphtext, d, n)
print ("The Plaintext is: "+Plaintext)
if __name__ == '__main__':
main()

五、MD5

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
# -*- coding = utf - 8 -*-
#@Time : 2020/12/18 21:50
#@Author : sunzy
#@File : MD5.py

import math
# 定义初始向量abcd,并将其转换成2进制,且补0到32位
# 标准的幻数(物理顺序)是(A=(01234567)16,B=(89ABCDEF)16,C=(FEDCBA98)16,D=(76543210)16)。如果在程序中定义应该是:
# (A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)
ABCD_list = ['67452301','efcdab89','98badcfe','10325476']
for i in range(len(ABCD_list)):
tmp = bin(int(ABCD_list[i], 16))[2:]
if len(tmp) < 32:
tmp = (32 - len(tmp)) * '0' + tmp
ABCD_list[i] = tmp
A0,B0,C0,D0 = ABCD_list[0], ABCD_list[1], ABCD_list[2], ABCD_list[3]

# 生成第1-64个式子的第i个32比特常数
Ti = []
for i in range(0, 64):
result = (int(4294967296 * abs(math.sin(i + 1)))) & 0xffffffff
result = bin(result)[2:]
if len(result) < 32:
result = (32 - len(result)) * '0' + result
Ti.append(result)

# 实现x,y的逐比特与
def and1(x, y):
res = ''
for i in range(0, len(x)):
res += str(int(x[i])&int(y[i]))
return res
# 实现x,y的逐比特或
def or1(x, y):
res = ''
for i in range(0, len(x)):
res += str(int(x[i])|int(y[i]))
return res
# 实现x,y的逐比特异或
def xor(x, y):
res = ''
for i in range(0, len(x)):
res += str(int(x[i])^int(y[i]))
return res
# 实现x的逐比特逻辑反
def reverse(x):
res = ''
for i in range(0, len(x)):
res += str((int(x[i], 2) + 1) % 2)
return res

# 实现x的循环左移
def shift(x, i):
res = ''
for t in range(0, len(x) - i):
res = res + x[t + i:t + i + 1] # 先保存 x[i:] 再保存x[:i]
for y in range(0, i):
res = res + x[y:y + 1]
return res

# 实现整数模2的三十二次方加法
def add(x, y):
a = int(x,2) #先将二进制转换成十进制
b = int(y,2)
res = (a + b)%(2**32)
res = str(bin(res)[2:])
if len(res) != 32: # 补充到32位
res = (32-len(res))*'0'+res
return res

# 定义f,g,h,i函数
def ffunc(x, y, z): # ((x&y)|((~x)&z))
t = and1(x, y)
t1 = reverse(x)
t2 = and1(t1, z)
return or1(t, t2)

def gfunc(x, y, z): # ((x&z)|(y&(~z)))
t = and1(x, z)
t1 = reverse(z)
t2 = and1(y, t1)
return or1(t, t2)

def hfunc(x, y, z): # (x^y^z)
t1 = xor(x, y)
t2 = xor(t1, z)
return t2

def ifunc(x, y, z): # (y^(x|(~z)))
t = reverse(z)
t1 = or1(x, t)
return xor(y, t1)

def fill(text):
text1 = ''
for i in text:
t = str(ord(i))
t = str(bin(int(t, 10))[2:])
if len(t) < 8: # 将每个字符转换成8位二进制数
for num in range(8 - len(t)):
t = '0' + t
text1 = text1 + t
length = len(text1)
length1 = 512 - len(text1) - 65 # length1是要填充0的位数
text1 = text1 + '1' # 第一位添加 0
text1 = text1 + '0'*length1 # 将其补充到 N*512+448 N可以为0
text2 = bin(length)[2:] # 将字符长度转换成二进制数
if len(text2) < 8: # 填充后面64位,先填充字符串的长度,再补0
text2 = '0'*(8 - len(text2)) + text2

length2 = 64 - len(text2)
text2 = text2 + '0'*length2 # 填充后面64位,先填充字符串的长度,再补0
return text1 + text2

# 将最后得到的ABCD逆序输出 最后一步使用
def reverse_order(a):
res = a[24:32] + a[16:24] + a[8:16] + a[0:8]
return res

# 输入要加密的明文
password = input("请输入要加密的信息:")
# 填充算法
x = fill(password)


j = 0
M = []
for i in range(0,len(x),32): # 将512位分成十六组
M.append(x[i:i+32])
M[j] = M[j][24:32] + M[j][16:24] + M[j][8:16] + M[j][0:8]
j+=1

# md5算法的第一步
A,B,C,D= A0,B0,C0,D0 #为第五步 相加原始的A,B,C,D做备份

# md5算法的第二步
AA,BB,CC,DD = A,B,C,D

# md5算法的第三步
# 第一轮 每轮A,B,C,D都处理四次,四轮就是十六次,一共六十四次
for i in range(4): # f函数
A = add(B, shift(add(A, add(add(ffunc(B, C, D), M[4*i]), Ti[4 * i])), 7))
D = add(A, shift(add(D, add(add(ffunc(A, B, C), M[4*i+1]), Ti[4 * i + 1])), 12))
C = add(D, shift(add(C, add(add(ffunc(D, A, B), M[4*i+2]), Ti[4 * i + 2])), 17))
B = add(C, shift(add(B, add(add(ffunc(C, D, A), M[4*i+3]), Ti[4 * i + 3])), 22))

# 第二轮
k = 1
j = 16
for i in range(4):
A = add(B, shift(add(A, add(add(gfunc(B, C, D), M[(k+5*(4*i))%16]), Ti[j + i * 4])), 5))
D = add(A, shift(add(D, add(add(gfunc(A, B, C), M[(k+5*(4*i+1))%16]), Ti[j + i * 4 + 1])), 9))
C = add(D, shift(add(C, add(add(gfunc(D, A, B), M[(k+5*(4*i+2))%16]), Ti[j + i * 4 + 2])), 14))
B = add(C, shift(add(B, add(add(gfunc(C, D, A), M[(k+5*(4*i+3))%16]), Ti[j + i * 4 + 3])), 20))

# 第三轮
k = 5
j = 32
for i in range(4):
A = add(B, shift(add(A, add(add(hfunc(B, C, D), M[(k+i*4*3)%16]), Ti[j + i * 4])), 4))
D = add(A, shift(add(D, add(add(hfunc(A, B, C), M[(k+(i*4+1)*3)%16]), Ti[j + i * 4 + 1])), 11))
C = add(D, shift(add(C, add(add(hfunc(D, A, B), M[(k+(i*4+2)*3)%16]), Ti[j + i * 4 + 2])), 16))
B = add(C, shift(add(B, add(add(hfunc(C, D, A), M[(k+(i*4+3)*3)%16]), Ti[j + i * 4 + 3])), 23))

# 第四轮
k = 0
j = 48
for i in range(4):
A = add(B, shift(add(A, add(add(ifunc(B, C, D), M[(k+(i*4)*7)%16]), Ti[j + i * 4])), 6))
D = add(A, shift(add(D, add(add(ifunc(A, B, C), M[(k+(i*4+1)*7)%16]), Ti[j + i * 4 + 1])), 10))
C = add(D, shift(add(C, add(add(ifunc(D, A, B), M[(k+(i*4+2)*7)%16]), Ti[j + i * 4 + 2])), 15))
B = add(C, shift(add(B, add(add(ifunc(C, D, A), M[(k+(i*4+3)*7)%16]), Ti[j + i * 4 + 3])), 21))
# 第五步 将计算出的A,B,C,D与初始的相加,并赋值
A,B,C,D = add(A, AA),add(B, BB),add(C, CC),add(D, DD)
# 输出得到的密文

ciphertext = reverse_order(A) + reverse_order(B) + reverse_order(C) + reverse_order(D)
cipher = ciphertext
ciphertext = str(hex(int(ciphertext, 2))[2:]) # 将二进制数转换为十六进制数
ciphertext = '0'*(32-len(ciphertext))+ciphertext # 为了避免第一个数字为零时无法显示出来
print("hash值(小写):",ciphertext)
print("hash值(大写):",ciphertext.upper())


六、综合实验

6.1 实验内容

现在,Alice想通过公共信道给Bob传输一份秘密文件(文件非常大)。又知道,很多人和机构想得到这份文件。需要设计一个通信模型,来保证文件的机密性和完整性。

现在很多人想要得到这份文件,那么,可能会有很多人在假冒Bob的身份,来请求得到这个文件,需要对Bob身份进行验证;也有可能是很多人得不到文件,就假冒Alice的身份,想要给Bob发送文件,如果有人发送恶意文件给Bob,Bob发现不是想要的文件,但这个文件给Bob电脑安装了后门,等他接受了正确的文件,就存在泄漏的风险,所以需要对Alice身份进行验证。

6.2 分析实验需求

现在很多人想要得到这份文件,那么,可能会有很多人在假冒Bob的身份,来请求得到这个文件,需要对Bob身份进行验证;也有可能是很多人得不到文件,就假冒Alice的身份,想要给Bob发送文件,如果有人发送恶意文件给Bob,Bob发现不是想要的文件,但这个文件给Bob电脑安装了后门,等他接受了正确的文件,就存在泄漏的风险,所以需要对Alice身份进行验证。

根据分析,所得结果如下:

  1. 传输的是秘密文件,所以可能存在人冒充bob接收文件,所以Alice在与传输文件之前需要对bob的身份进行认证,而且bob防止被欺骗也需要对Alice的身份进行认证,这里可以采用第二类签名算法。

  2. 在确认彼此身份后需要将文件通过公共信道传输,但是可能存在攻击人劫持,所以需要对文件内容加密,而文件内容很大,考虑到加密速度问题,所以采用对称加密算法。

  3. 采用对称加密算法需要密钥,而这里使用随机生成的密钥。并且这个密钥也需要传给bob用于解密,但是公共信道不安全,所以也需要对该密钥进行加密。考虑到密钥长度不是很大,所以可以采用公钥加密算法,而且公钥加密算法可以解决传输此次加密使用的密钥,提高安全性和效率。

  4. Bob收到密文和加密后的密钥,使用自己的私钥解密出对称加密算法使用的密钥,然后使用密钥解密密文后向Alice发送确认消息,确保bob收到消息。

  5. 确认消息也要使用到第一步使用的签名算法。

6.3 程序流程图

image-20210202142616269

6.4 完整代码

Alice

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
# -*- coding = utf - 8 -*-
#@Time : 2021/1/3 23:17
#@Author : sunzy
#@File : Alice.py

from RSA import *
from DES import *
from MD5 import *
from file_decode import *
import socket

n_bob = 81836285346168841919828227774710209132464519960137459236348092425786962849543
n = 44531776921047477359676235110843825307036514195195627878765712056028758572817
d = 34159352569920789505556306994405309761109146525598570740717995833480670158209
e = 65537

name = "This is alice."

client = socket.socket()
client.connect(('127.0.0.1',8888)) # 本机地址和端口

data = client.recv(1024)
print(str(data, "utf-8"),end="")
client.send(b'1')
print("等待bob验证自身身份...")

# alice送出自己的身份信息和签名值
client.send(bytes(name,'utf-8')) # alice发送身份明文信息
client.recv(1024).decode()
hash_name = md5(name)
crpto_name = str(RSAEncode(hash_name, d, n)) # 发送对hash值加密后的值
crpto_name = bytes(crpto_name,'utf-8')
client.send(crpto_name)

print(client.recv(1024).decode()) # 接收身份认证的结果
#接收bob身份hash并验证
print("验证bob身份...",end="")
a=input()

bob_name = client.recv(512).decode() # bob发送过来的身份明文信息
client.send(b'1')

hash_name = client.recv(512).decode() # bob使用自己的私钥加密身份明文信息hash值后的值
plain_text = RSADecode(int(hash_name),e,n_bob) # 使用bob的公钥解密上一步的值

client.send(b'1')
if plain_text == md5(bob_name): # 验证是不是bob
print(bob_name)
print("验证通过!")
else:
exit("验证错误!")


while 1:
a = input()
bin_key = gen_key() # 随机生成64位的 DES加密明文使用的密钥
int_key = int(bin_key,2) # 将其转换为十进制数,便于后面使用RSA算法加密
print("随机产生对称密钥:",int_key) # 每次传输随机产生一个密钥更加安全

plain = read_file() # 读取文件内容
bin_cipher = DES_encode(plain, bin_key) # 使用DES算法加密内容

a = input()
int_key = fast_mod(int_key,e,n_bob) # 加密DES的密钥 e是bob的公钥
print("加密对称密钥")

print("传输密钥...") # 传输加密后DES密钥
client.sendall(bytes(str(int_key),"utf-8"))
client.recv(512)

length = str(len(bin_cipher)) # 传输发送内容的长度
length = bytes(length,"utf-8") # 方便其接收
client.send(length)

print("传输密文...")
client.sendall(bytes(str(bin_cipher),"utf-8")) #发送密文
message = client.recv(512).decode()
client.send(b'1')
crypt_message = client.recv(512).decode()
hash_message = RSADecode(int(crypt_message),e,n_bob)
if hash_message == md5(message):
print(message)
print("文件传输结束!")
break
client.close()

Bob

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
# -*- coding = utf - 8 -*-
#@Time : 2021/1/3 23:17
#@Author : sunzy
#@File : bob.py

import socket
import libnum
from RSA import *
from DES import *
from MD5 import *

name = "This is bob."


n = 81836285346168841919828227774710209132464519960137459236348092425786962849543
n_alice = 44531776921047477359676235110843825307036514195195627878765712056028758572817
d = 39540198292360595989919600111176873707392294831248672388670326288876701993673
e = 65537

# e_alice = 2081

server = socket.socket() #创建对象
server.bind(("localhost",8888))#绑定 ip和端口
server.listen() #监听
#等待连接
print('Waiting connection...')
#接受请求,返回套接字对象和IP+端口号
con,addr = server.accept()
con.send(bytes("Welcome connect!\n开始加密传输:\n","utf-8"))
con.recv(1024)

# 验证alice身份
print("验证alice身份...")
a = input()
alice_name = con.recv(512).decode() # alice 的明文信息
con.send(b'1')
hash_name = con.recv(512).decode() # alice 用私钥加密明文的hash值
plain_text = RSADecode(int(hash_name),e,n_alice) # 用alice 的公钥解密出hash值
if plain_text == md5(alice_name): # 判断上一步的值与md5函数加密是否相同
print(alice_name) # 如果二者相同则可以保证对方是alice,这里的安全性是由公钥算法和hash函数保证
print("验证通过!") # 因为只有alice有自己的私钥,hash函数的存在防止伪造明文攻击
con.send(bytes("您通过了验证!","utf-8"))
else:
exit("这不是alice,验证错误!")
#送出自己的身份信息和签名值
print("等待alice验证自身身份...")

con.send(bytes(name,'utf-8')) # 向alice发送身份信息的明文
con.recv(1024).decode()

hash_name = md5(name) # 明文信息的hash值
crpto_name = str(RSAEncode(hash_name, d, n)) # 使用自己的私钥加密上一步的hash值
crpto_name = bytes(crpto_name,'utf-8') # 发送给alice
con.send(crpto_name)
con.recv(1024)

while 1:
data = con.recv(1024) # 接收alice发送的使用公钥算法加密后的DES的密钥
data = data.decode()
int_key = int(data)
print("被加密后的密钥:",int_key)
int_key = fast_mod(int_key, d, n) # 使用私钥d解密出DES密钥
print("解密后的密钥", int_key)

print("----"*10) # 接收密文的长度(为了方便存储)
con.send(b'1')
length = con.recv(1024)
length = int(length.decode())
buff = []
size = 0
while size < length: # 开始接受密文
dat = con.recv(1024)
size += len(dat.decode())
buff.append(dat)
data = b''.join(buff)
bin_cipher = data.decode()

cipher = bin_to_str(bin_cipher)
bin_key = bin(int_key)[2:] # 将密钥转换成二进制数
print("解密传输内容:")
print(libnum.b2s(DES_decode(cipher, bin_key))) #打印出解密后的明文
message = "Bob received the file successfully!"
con.send(bytes(message,'utf-8'))
con.recv(1024).decode()
hash_message = md5(message)
crypt_message = str(RSAEncode(hash_message,d,n))
con.send(bytes(crypt_message,'utf-8'))
break

server.close()

file_decode

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
# -*- coding = utf - 8 -*-
#@Time : 2021/1/3 23:17
#@Author : sunzy
#@File : file_decode.py


import random
import socket
import re
from RSA import *
from DES import *
from MD5 import *

def gen_key(): # 生成64位的对称加密的密钥
list = []
for i in range(64):
c = random.choice(['0', '1'])
list.append(c)
res = "".join(list)
return res

def read_file():
try:
f = open('text.txt','r', encoding = 'utf-8')
text = f.read()
f.close()
print("读取成功!")
return text
except IOError:
print("读取错误!")


def bin_to_str(bin_str) : # 8位二进制转字符,用于bob收到密后使用
res = ""
tmp = re.findall(r'.{8}',bin_str)
for i in tmp :
res += chr(int(i,2))
return res