古典密码代码
今天开始上密码课,才想起了之前寒假一时兴起写了古典密码的加密解密python代码,故在此总结下
移位密码
def run():
print('*'*5 + '移位密码' + '*'*5)
choice = input('请选择加密/解密(e/d):')
move = int(input('输入右移位数:'))
if choice == 'E' or choice == 'e':
encrypt(move)
elif choice == 'D' or choice == 'd':
decrypt(move)
else:
print('输入有误')
# 加密
def encrypt(move):
m = input('请输入明文:')
print('加密开始……')
c = ''
for i in m: # 遍历明文字符,进行对应操作
if ord(i) == 32: # 空格
c = c + ' '
elif ord(i) in range(65, 91): # 大写
c = c + chr(65 + (ord(i) - 65 + move) % 26)
elif ord(i) in range(97, 123): # 小写
c = c + chr(97 + (ord(i) - 97 + move) % 26)
else: # 其他字符
c = c + i
print('密文:' + c)
# 解密
def decrypt(move):
c = input('请输入密文:')
m = ''
print('解密开始……')
for i in c:
if ord(i) == 32:
m = m + ' '
elif ord(i) in range(65, 91):
m = m + chr(65 + (ord(i) - 65 - move) % 26)
elif ord(i) in range(97, 123):
m = m + chr(97 + (ord(i) - 97 - move) % 26)
else:
m = m + i
print('明文:' + m)
if __name__ == '__main__':
run()
凯撒密码
导入移位密码文件,设置move为3即可
import Transposition as yiwei
def run():
print('*' * 5 + '凯撒密码' + '*' * 5)
choice = input('请选择加密/解密(e/d):')
move = 3
if choice == 'E' or choice == 'e':
yiwei.encrypt(move)
elif choice == 'D' or choice == 'd':
yiwei.decrypt(move)
else:
print('输入有误')
if __name__ == '__main__':
run()
仿射密码
线性变换,需要输入k1、k0
def run():
print('*' * 5 + '仿射密码' + '*' * 5)
k1 = int(input('请输入k1:'))
k0 = int(input('请输入k0:'))
choice = input('请选择加密/解密(e/d):')
if choice == 'E' or choice == 'e':
encrypt(k0, k1)
elif choice == 'D' or choice == 'd':
decrypt(k0, k1)
else:
print('输入有误')
def encrypt(k0, k1):
m = input('请输入明文:')
c = ''
print('加密开始……')
for i in m:
if m.isspace():
c = c + ' '
elif i.islower():
c = c + chr(((ord(i) - 97) * k1 + k0) % 26 + 97)
elif i.isupper():
c = c + chr(((ord(i) - 65) * k1 + k0) % 26 + 65)
else:
c = c + i
print('密文:' + c)
def decrypt(k0, k1):
c = input('请输入密文:')
m = ''
print('解密开始……')
inv = 0
for i in range(26):
if k1 * i % 26 == 1:
inv = i
break
for i in c:
if c.isspace():
m = m + ' '
elif i.islower():
m = m + chr(((ord(i) - 97 - k0) * inv) % 26 + 97)
elif i.isupper():
m = m + chr(((ord(i) - 65 - k0) * inv) % 26 + 65)
else:
m = m + i
print('明文:' + m)
if __name__ == '__main__':
run()
密钥短语密码
def run():
print('*' * 5 + '密钥短语密码(小写)' + '*' * 5)
choice = input('请选择加密/解密(e/d):')
key = list(input('输入密钥短语:'))
keys = create_k(key)
if choice == 'E' or choice == 'e':
encrypt(keys)
elif choice == 'D' or choice == 'd':
decrypt(keys)
else:
print('输入有误')
# 创建密钥列表
def create_k(key):
keys = []
for i in key:
if i not in keys: # 如果已存在keys列表中,则不添加
keys.append(i)
for j in range(97, 123): # 按a-z顺序添加,除非已存在
apha = chr(j)
if apha not in keys:
keys.append(apha)
print('Your key:' + ''.join(keys))
return keys
def encrypt(keys):
m = input('请输入明文:')
c = ''
print('加密开始……')
for i in m:
if i.isspace():
c = c + ' '
else:
c = c + keys[ord(i) - 97] # 将明文字符与新的26字母表,即keys相对应
print('密文:' + c)
def decrypt(keys):
c = input('请输入密文:')
m = ''
print('解密开始……')
for i in c:
if i.isspace():
m = m + ' '
else:
m = m + chr(keys.index(i) + 97)
print('明文:' + m)
if __name__ == '__main__':
run()
维吉尼亚密码
def run():
print('*' * 5 + '维吉尼亚密码' + '*' * 5)
choice = input('请选择加密/解密(e/d):')
k = input('请输入密钥:')
if choice == 'E' or choice == 'e':
encrypt(k)
elif choice == 'D' or choice == 'd':
decrypt(k)
else:
print('输入有误')
def encrypt(k):
m = input('请输入明文:')
c = ''
print('加密开始……')
for i in range(len(m)):
if m[i].isspace():
c = c + ' '
elif m[i].islower():
c = c + chr((ord(m[i]) - 97 + ord(k[i % len(k)]) - 97) % 26 + 97)
elif m[i].isupper():
c = c + chr((ord(m[i]) - 65 + ord(k[i % len(k)]) - 65) % 26 + 65)
else:
c = c + m[i]
print('密文:' + c)
def decrypt(k):
c = input('请输入密文:')
m = ''
print('解密开始……')
for i in range(len(c)):
if c[i].isspace():
m = m + ' '
elif c[i].islower():
m = m + chr((ord(c[i]) - ord(k[i % len(k)])) % 26 + 97)
elif c[i].isupper():
m = m + chr((ord(c[i]) - ord(k[i % len(k)])) % 26 + 65)
print('明文:' + m)
if __name__ == '__main__':
run()
希尔密码
这部分比较难写代码,在网上搜集了很多学习资料,最后才发现比较符合自己想法的文章https://hstar.me/2020/08/hill-cipher-study/
算法如图:
然后稍微复习了下线性代数:https://www.shuxuele.com/algebra/matrix-inverse-minors-cofactors-adjugate.html
但是这里的算法并不是简单的求矩阵逆,还要与替换表的长度26进行相应的模运算,网上很多都是简单的求逆矩阵
快速求模乘法逆:https://www.delftstack.com/zh/howto/python/mod-inverse-python/,使用pow()内置函数:pow(a,-1,m),a为要找到模逆的数,m为模数
还有个比较关键的细节:关于python的取整函数
- int:去除小数部分,即只保留前面的整数,向0取整
- round:遵循四舍五入原则,本例中应使用该法
import numpy as np # 与矩阵计算相关的库
def run():
print('*' * 5 + '希尔密码' + '*' * 5)
choice = input('请选择加密/解密(e/d):')
if choice == 'E' or choice == 'e':
encrypt()
elif choice == 'D' or choice == 'd':
decrypt()
else:
print('输入有误')
# 创建密钥矩阵
def create_k():
print('**输入密钥模式**')
print('1:数字密钥;2:字母密钥')
choice = int(input('请选择模式(数字):'))
n = int(input('请输入密钥矩阵的行数:'))
k_list = []
print('请输入' + str(n) + '*' + str(n) + '可逆矩阵(每行' + str(n) + '个,空格隔开):')
for i in range(1, n + 1):
while True:
row = input(f'第{i}行:')
row_list = row.split(' ')
if choice == 1:
row_list = [int(j) for j in row_list]
elif choice == 2:
row_list = [ord(j) - 97 for j in row_list]
# 判断矩阵每行个数是否正确
if len(row_list) != n:
print('输入个数错误,请重新输入:')
else:
break
k_list.append(row_list)
# print(k_list)
if if_inv_matrix(k_list):
return np.array(k_list)
else:
print('不存在逆矩阵!')
# 判断矩阵是否存在逆
def if_inv_matrix(k_list):
try:
np.linalg.inv(k_list)
except:
return False
return True
def encrypt():
m = input('请输入明文:')
print('加密开始……')
c = ''
m_list = []
for i in m:
m_list.append(ord(i) - 97)
k = create_k()
# 检查并补齐明文,使之满足n维向量与n*n矩阵相乘
row_num = len(k)
add_m_num = (row_num - len(m_list) % row_num) % row_num
if add_m_num != 0:
for n in range(add_m_num):
m_list.append(0)
print(f'添加了{add_m_num}个0补齐明文')
# 分组乘积
for j in range(int(len(m_list) / row_num)):
# 线性代数乘积
M = m_list[j * row_num: (j + 1) * row_num]
c_list = np.matmul(k, M)
c = c + ''.join([chr(x % 26 + 97) for x in c_list])
# print(c[:len(c) - add_m_num])
print('密文:' + c)
# 求最大公因数
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def decrypt():
c = input('请输入密文:')
m = ''
print('解密开始……')
c_list = []
for i in c:
c_list.append(ord(i) - 97)
k = create_k()
row_num = len(k)
det_k = np.linalg.det(k) # 获取密钥矩阵的行列式
inv_det_k = pow(int(det_k), -1, 26) # 求密钥矩阵行列式模逆
if gcd(inv_det_k, 26) != 1:
print('密钥矩阵行列式与26不互质,无法解密!')
return
elif inv_det_k == 0:
print('密钥矩阵行列式不能为0!')
return
inv_k = inv_det_k * np.linalg.inv(k) * det_k % 26 # 求希尔密码密钥矩阵的逆矩阵(同上公式)
for j in range(int(len(c_list) / row_num)):
# 线性代数乘积
C = np.array(c_list[j * row_num: (j + 1) * row_num])
# print(C)
m_list = np.matmul(inv_k, C)
# 这里一定要注意不要用int来取整,他这里省略了小数部分,应该用近似round,血泪教训!
m = m + ''.join([chr(round(x) % 26 + 97) for x in m_list])
print('明文:' + m + '(由于可能存在补齐的明文,请自行去掉末尾可能多余的a)')
if __name__ == '__main__':
run()
Playfair密码
这个是真复杂,难想,一直算出来的与在线网站加密解密不一样,最后才发现原来有两种主流方式
- 在重复数字后加‘x’
- 第二个重复数字改成‘x’
第一种是我的法和普莱费尔密码加密/解密 - 一个工具箱 - 好用的在线工具都在这里! (atoolbox.net)这个网址的方法
第二种是pycipher库下的Playfair的方法和CTF在线工具-在线普莱菲尔密码加密|在线普莱菲尔密码解密|普莱菲尔密码算法|Playfair Cipher (hiencode.com)和普莱菲尔密码 - Playfair Cipher - 在线工具网 (wtool.com.cn)等的方法
最后验证出自己结果正确时真高兴,shit,手算了老多遍了
def run():
print('*' * 5 + 'Playfair密码' + '*' * 5)
choice = input('请选择加密/解密(e/d):')
if choice == 'E' or choice == 'e':
encrypt()
elif choice == 'D' or choice == 'd':
decrypt()
else:
print('输入有误')
# 初始化5*5矩阵
def create_k():
key = input('请输入密钥:')
key = key.replace(' ', '')
keys = [] # 创建密钥列表
for i in key:
if i not in keys:
if i == 'j': #
keys.append('i')
else:
keys.append(i)
for j in range(97, 123):
apha = chr(j)
if apha not in keys:
if apha == 'j':
if 'i' not in keys:
keys.append('i')
else:
continue
else:
keys.append(apha)
# 创建密钥矩阵
# 初始化5*5矩阵,这里构建不能采用[[0]*5]*5,否则后面赋值错误,会变换整列
k_arr = [[0] * 5 for i in range(5)]
k = 0
print('\t**密钥矩阵**')
for i in range(5):
for j in range(5):
k_arr[i][j] = keys[k]
print(keys[k], end='\t')
if (k + 1) % 5 == 0:
print('')
k += 1
return k_arr
# 获取字符对应矩阵的位置
def get_index(p, k_arr):
if p == 'j':
p = 'i'
for i in range(len(k_arr)):
for j in range(len(k_arr)):
if k_arr[i][j] == p:
return i, j
def encrypt():
m = input('请输入明文:')
m = m.replace(' ', '') # 去除空格
print('加密开始……')
c = ''
c_list = []
k_arr = create_k()
for i in range(0, len(m) + 1, 2):
if i < len(m) - 1:
if m[i] == m[i + 1]:
# m = m[:i + 1] + 'x' + m[i + 2:] # 第二个重复字母改成‘x’
m = m[:i + 1] + 'x' + m[i + 1:] # 在重复字母后加‘x’
if len(m) % 2 != 0:
m = m + 'x'
print('整理后的明文:' + m)
for j in range(0, len(m), 2):
P1 = get_index(m[j], k_arr)
P2 = get_index(m[j+1], k_arr)
print(P1, P2)
if P1[0] == P2[0]: # P1、P2在同一行
C1 = k_arr[P1[0]][(P1[1] + 1) % 5]
C2 = k_arr[P2[0]][(P2[1] + 1) % 5]
elif P1[1] == P2[1]: # P1、P2在同一列
C1 = k_arr[(P1[0] + 1) % 5][P1[1]]
C2 = k_arr[(P2[0] + 1) % 5][P2[1]]
else:
C1 = k_arr[P1[0]][P2[1]]
C2 = k_arr[P2[0]][P1[1]]
c_list.append(C1 + C2)
c = ''.join(c_list)
print('密文:' + c)
def decrypt():
c = input('请输入密文:')
m = ''
print('解密开始……')
m_list = []
k_arr = create_k()
for j in range(0, len(c), 2):
C1 = get_index(c[j], k_arr)
C2 = get_index(c[j+1], k_arr)
if C1[0] == C2[0]: # C1、C2在同一行
P1 = k_arr[C1[0]][(C1[1] - 1) % 5]
P2 = k_arr[C2[0]][(C2[1] - 1) % 5]
elif C1[1] == C2[1]: # C1、C2在同一列
P1 = k_arr[(C1[0] - 1) % 5][C1[1]]
P2 = k_arr[(C2[0] - 1) % 5][C2[1]]
else:
P1 = k_arr[C1[0]][C2[1]]
P2 = k_arr[C2[0]][C1[1]]
m_list.append(P1 + P2)
m = ''.join(m_list)
# 删除添加的x,但是无法准确判断结尾x是否存在明文中,需要结合语义进行判断
for i in range(len(m)):
if 0 < i < len(m) - 1 and m[i] == 'x' and m[i - 1] == m[i + 1]:
m = m[:i] + m[i+1:]
print('明文:' + m + '(若结尾存在x,请自行判断x是否需要存在)')
if __name__ == '__main__':
run()
总代码:
import Transposition as yiwei
import Caesar
import Affine
import KeyPhrase as kp
import Vigenere as vig
import Hill
import Playfair as pf
def run():
while True:
print('*'*10 + 'Powered By Xherlock' + '*'*10)
print(' ' * 10 + '1.移位密码')
print(' ' * 10 + '2.凯撒密码')
print(' ' * 10 + '3.仿射密码')
print(' ' * 10 + '4.密钥短语密码')
print(' ' * 10 + '5.维吉尼亚密码')
print(' ' * 10 + '6.希尔密码')
print(' ' * 10 + '7.Playfair密码')
choice = int(input('请选择密码种类:'))
if choice == 1:
yiwei.run()
elif choice == 2:
Caesar.run()
elif choice == 3:
Affine.run()
elif choice == 4:
kp.run()
elif choice == 5:
vig.run()
elif choice == 6:
Hill.run()
elif choice == 7:
pf.run()
print('\n')
if __name__ == '__main__':
run()