前言
这是之前的密码学课设,因为一直在忙期末考试,所以到现在才发出来。exe程序可以去希尔密码(Hill密码)转换工具.exe下载。
算法原理
每个字母当作26进制数字:A=0, B=1, C=2… 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果模26。 注意用作加密的矩阵(即密钥)必须是可逆的,否则就不可能解码。只有矩阵的行列式和26互质,才是可逆的。
加解密代码
import numpy as np
import sys
def judge_inverse_matrix(matrix):
try:
np.linalg.inv(matrix)
except:
return False
return True
def inputmatrix():
row_num = int(input("请输入矩阵的行数:"))
all_list = []
for i in range(1, row_num + 1):
row = input(f"请输入加密矩阵第{i}行(以空格为分隔):")
if row[0] == ' ':
print("输入有误,第一位不该为空格")
sys.exit()
else:
row_list = row.split(' ')
if len(row_list) == row_num:
for n in row_list:
row_list[row_list.index(n)] = int(row_list[row_list.index(n)])
all_list.append(row_list)
else:
print("前后输入的行数不一致,请重修输入")
break
encrypt_matrix = np.array(all_list)
if not judge_inverse_matrix(encrypt_matrix):
print("该矩阵不存在逆矩阵,请重修输入")
return encrypt_matrix
def generate_inverse_matrix(matrix):
inverse_matrix = np.linalg.inv(matrix)
for row in inverse_matrix:
for num in row:
num = round(num)
print("加密矩阵的逆矩阵为:")
for array in inverse_matrix:
print(array)
return inverse_matrix
def alphabet_number():
alphabet_number_dict = {}
for i in range(97, 123):
alphabet_number_dict[chr(i)] = i % 97
return alphabet_number_dict
def encrypt():
input_plaintext = input("请输入明文:")
num_list = []
dic = alphabet_number()
for i in input_plaintext:
num_list.append(dic[i])
matrix = inputmatrix()
row_num = len(matrix)
supple_num = row_num - (len(num_list) % row_num)
if len(num_list) % row_num != 0:
for n in range(1, supple_num + 1):
num_list.append(25)
print(f"\n添加了{supple_num}个z补全明文")
group_num = int(len(num_list) / row_num)
whole_encrypt_num_list = []
for g in range(0, group_num):
plaintext_matrix = np.array(num_list[0 + g * row_num: (g + 1) * row_num])
encrypt_num_list = np.matmul(plaintext_matrix, matrix)
for num in encrypt_num_list:
whole_encrypt_num_list.append(num)
ciphertext = ""
for ennum in whole_encrypt_num_list:
if ennum > 25:
ennum = ennum % 26
for k in dic:
if dic[k] == ennum:
ciphertext = ciphertext + k
print("加密后密文为:", ciphertext, '\n')
def decrypt():
input_ciphertext = input("请输入密文:")
num_list2 = []
dic2 = alphabet_number()
for i in input_ciphertext:
num_list2.append(dic2[i])
matrix = inputmatrix()
row_num2 = len(matrix)
supple_num2 = row_num2 - (len(num_list2) % row_num2)
inserve_matrix = generate_inverse_matrix(matrix)
group_num2 = int(len(num_list2) / row_num2)
whole_decrypt_num_list = []
for g in range(0, group_num2):
plaintext_matrix = np.array(num_list2[0 + g * row_num2: (g + 1) * row_num2])
decrypt_num_list = np.matmul(plaintext_matrix, inserve_matrix)
for num in decrypt_num_list:
whole_decrypt_num_list.append(num)
plaintext = ""
for denum in whole_decrypt_num_list:
if denum > 25 or denum < -26:
denum = denum % 26
if denum < 0:
denum = denum + 26
for k in dic2:
if dic2[k] == denum:
plaintext = plaintext + k
print("解密后明文为:", plaintext, '\n')
if __name__ == '__main__':
while True:
print("========Hill密码========\n")
print("1.加密\n2.解密\n")
print("注意:如果输入矩阵的逆矩阵中含有小数,采用四舍五入的方法\n")
pattern = input("请选择模式:")
if pattern == '1':
encrypt()
elif pattern == '2':
decrypt()
else:
print("输入有误,请重修输入")
- 这里主要是采用了
numpy 模块来实现判断输入矩阵的逆矩阵存在与否以及矩阵间的乘法; - 同时,因为输入的明文可能在分组后的列无法与矩阵的行数相等,所以当不相等时,自动在明文后用字母
z 补全; - 还有一个主要的问题就是输入矩阵的逆矩阵可能存在小数,因此这里采用了
四舍五入 的方法,当输入的矩阵数字过大时,可能会造成解密结果不准确。
图形界面代码
import numpy as np
import sys
from tkinter import *
class MY_GUI():
def __init__(self, init_window_name):
self.init_window_name = init_window_name
def set_init_window(self):
self.init_window_name.title("Hill密码转换工具")
self.init_window_name.geometry('700x500+300+120')
self.init_window_name["bg"] = "Lavender"
self.crypto_string_label = Label(self.init_window_name, text="加/解密字符串", font=13)
self.crypto_string_label.place(x=80, y=60)
self.row_num_label = Label(self.init_window_name, text="矩阵行数", font=13)
self.row_num_label.place(x=120, y=120)
self.encrypt_matrix_label = Label(self.init_window_name, text="加密矩阵", font=13)
self.encrypt_matrix_label.place(x=120, y=220)
self.encrypt_matrix_label = Label(self.init_window_name, text="(空格分隔)", font=13)
self.encrypt_matrix_label.place(x=110, y=240)
self.crypto_result_label = Label(self.init_window_name, text="加/解密结果", font=13)
self.crypto_result_label.place(x=100, y=330)
self.supple_string_label = Label(self.init_window_name, text="字符增补记录", font=13)
self.supple_string_label.place(x=90, y=400)
self.crypto_string_Text = Text(self.init_window_name, width=40, height=2)
self.crypto_string_Text.place(x=220, y=60)
self.row_num_Text = Text(self.init_window_name, width=40, height=2)
self.row_num_Text.place(x=220, y=120)
self.encrypt_matrix_Text = Text(self.init_window_name, width=40, height=9)
self.encrypt_matrix_Text.place(x=220, y=180)
self.crypto_result_Text = Text(self.init_window_name, width=40, height=2)
self.crypto_result_Text.place(x=220, y=330)
self.supple_string_Text = Text(self.init_window_name, width=40, height=2)
self.supple_string_Text.place(x=220, y=400)
self.encrypt_button = Button(self.init_window_name, text="加密", bg="LavenderBlush", width=10,
command=self.encrypt)
self.encrypt_button.place(x=580, y=150)
self.decrypt_button = Button(self.init_window_name, text="解密", bg="LavenderBlush", width=10,
command=self.decrypt)
self.decrypt_button.place(x=580, y=300)
def judge_inverse_matrix(self, matrix):
try:
np.linalg.inv(matrix)
except:
return False
return True
def inputmatrix(self):
row_num = int(self.row_num_Text.get("1.0", "end"))
all_list = []
num_list2 = []
num_list = self.encrypt_matrix_Text.get("1.0", "end").split(' ')
for a in num_list:
num_list2.append(int(a.strip('\n')))
for num in num_list2:
num_list2[num_list2.index(num)] = int(num_list2[num_list2.index(num)])
for i in range(0, len(num_list2), row_num):
all_list.append(num_list2[i: i + row_num])
encrypt_matrix = np.array(all_list)
if not self.judge_inverse_matrix(encrypt_matrix):
self.crypto_result_Text.delete("1.0", "end")
self.crypto_result_Text.insert("1.0", "该矩阵不存在逆矩阵,请重修输入")
return encrypt_matrix
def generate_inverse_matrix(self, matrix):
inverse_matrix = np.linalg.inv(matrix)
print("加密矩阵的逆矩阵为:")
for array in inverse_matrix:
print(array)
return inverse_matrix
def alphabet_number(self):
alphabet_number_dict = {}
for i in range(97, 123):
alphabet_number_dict[chr(i)] = i % 97
return alphabet_number_dict
def encrypt(self):
input_plaintext = self.crypto_string_Text.get("1.0", "end").strip('\n')
num_list = []
dic = self.alphabet_number()
for i in input_plaintext:
num_list.append(dic[i])
matrix = self.inputmatrix()
row_num = len(matrix)
supple_num = row_num - (len(num_list) % row_num)
if len(num_list) % row_num != 0:
for n in range(1, supple_num + 1):
num_list.append(25)
output = f"添加了{supple_num}个z补全明文"
self.supple_string_Text.delete("1.0", "end")
self.supple_string_Text.insert("1.0", output)
group_num = int(len(num_list) / row_num)
whole_encrypt_num_list = []
for g in range(0, group_num):
plaintext_matrix = np.array(num_list[0 + g * row_num: (g + 1) * row_num])
encrypt_num_list = np.matmul(plaintext_matrix, matrix)
for num in encrypt_num_list:
whole_encrypt_num_list.append(num)
ciphertext = ""
for ennum in whole_encrypt_num_list:
if ennum > 25:
ennum = ennum % 26
for k in dic:
if dic[k] == ennum:
ciphertext = ciphertext + k
self.crypto_result_Text.delete("1.0", "end")
self.crypto_result_Text.insert("1.0", ciphertext)
def decrypt(self):
input_ciphertext = self.crypto_string_Text.get("1.0", "end").strip('\n')
num_list2 = []
dic2 = self.alphabet_number()
for i in input_ciphertext:
num_list2.append(dic2[i])
matrix = self.inputmatrix()
row_num2 = len(matrix)
supple_num2 = row_num2 - (len(num_list2) % row_num2)
inserve_matrix = self.generate_inverse_matrix(matrix)
group_num2 = int(len(num_list2) / row_num2)
whole_decrypt_num_list = []
for g in range(0, group_num2):
plaintext_matrix = np.array(num_list2[0 + g * row_num2: (g + 1) * row_num2])
decrypt_num_list = np.matmul(plaintext_matrix, inserve_matrix)
for num in decrypt_num_list:
whole_decrypt_num_list.append(num)
for j in range(len(whole_decrypt_num_list)):
whole_decrypt_num_list[j] = round(whole_decrypt_num_list[j])
plaintext = ""
for denum in whole_decrypt_num_list:
if denum > 25 or denum < -26:
denum = denum % 26
if denum < 0:
denum = denum + 26
for k in dic2:
if dic2[k] == denum:
plaintext = plaintext + k
self.crypto_result_Text.delete("1.0", "end")
self.crypto_result_Text.insert("1.0", plaintext)
self.supple_string_Text.delete("1.0", "end")
def gui_start():
init_window = Tk()
ZMJ_PORTAL = MY_GUI(init_window)
ZMJ_PORTAL.set_init_window()
init_window.mainloop()
if __name__ == '__main__':
gui_start()
运行效果图
加密: 解密:
遇到的问题及解决方案
当时最主要是遇到了以下的几个问题:
-
当时用的win10,python自带的pyinstaller编译成exe运行,打包过后程序太大,有160MB? 答:原因是电脑中的python文件太多了,pyinstaller编译时把所有文件全部编译到exe程序中,导致程序占用空间过大,只需要在全新的虚拟机中编译,并用upx压缩,文件就只剩15MB左右了。 -
win7电脑运行exe程序时出现api-ms-win-core-path-l1-1-0.dll 丢失? 答:python3.9(最新版本)已经不支持win7操作系统了,可以选择低一些的版本(python3.7)来重新把py文件编译成exe程序。 -
在不同电脑上打开exe程序时,按键、文字分布在不同地方? 答:原因是每台电脑的分辨率不同。
|