前言
记录一下古典密码
单表代换密码
凯撒密码
一种替换密码,即将明文中的每个字母都替换为字母表中固定数量的字母
例如,如果将明文中的每个字母向右移动3个位置,那么字母”A”将被替换为字母”D”,字母”B”将被替换为字母”E”,以此类推。这个固定的移动量被称为密钥或偏移量。
数学原理
以上面那个例子为例,代换如下:
c = E3(m) ≡ m+3(mod 26),0 ≤ m ≤ 25
m = D3(c) ≡ c-3(mod 26),0 ≤ c ≤ 25
例子
假设我们要加密明文”HELLO”并使用偏移量3。首先,我们将明文中的每个字母向右移动3个位置。这将产生密文”KHOOR”。解密就是将密文中的每个字母向左移动3个位置,即将偏移量取反,以还原原始明文。
解密脚本
def caesar_decrypt(ciphertext, shift):
"""
凯撒密码解密函数
:param ciphertext: 密文
:param shift: 移位数
:return: 明文
"""
plaintext = ""
for char in ciphertext:
if char.isalpha():
# 将字母转换为ASCII码,并减去65或97,使得A或a的ASCII码为0
ascii_code = ord(char) - shift
# 处理超出字母表范围的ASCII码
if char.isupper():
if ascii_code < 65:
ascii_code += 26
else:
if ascii_code < 97:
ascii_code += 26
# 将ASCII码转换回字符
plaintext += chr(ascii_code)
else:
plaintext += char
return plaintext
ciphertext = ""
# ciphertext = ''.join([chr(i) for i in [86, 116, 128, 80, 98, 85, 139, 122, 134, 114, 125, 136, 117, 123, 129, 127, 128, 128, 142, 130, 140, 147, 127, 132, 131, 136, 151, 134, 152, 164]])
for i in range(1, 30):
shift = i
plaintext = caesar_decrypt(ciphertext, shift)
print(plaintext)
移位变换
和凯撒密码相比,移位变换就是把移动的位数改成了变量 k
即:
c = Ek(m) ≡ m+k(mod 26),0 ≤ m ≤ 25
m = Dk(c) ≡ c-k(mod 26),0 ≤ c ≤ 25
对应的解密脚本:
def decrypt(enc, shift):
dec = ""
for char in enc:
temp = ord(char) - shift
if char.isupper():
if temp < 65:
temp += 26
else:
if temp < 97:
temp += 26
dec += chr(temp)
return dec
enc = "BEEAKFYDJXUQYHYJIQRYHTYJIQFBQDUYJIIKFUHCQD"
for i in range(1, 25):
key = i
flag = decrypt(enc, key)
print(flag + " " + str(key))
仿射变换
在前面的基础上又在 m 前多了个倍数a,此时解密就要用到a的逆元了
c = Ea,b(m) ≡ am+b(mod 26),0 ≤ m ≤ 25
m = Da,b(c) ≡ a-1(c-b) (mod 26),0 ≤ c ≤ 25
则解密的流程为:
- 求a的逆元,python的
Crypto.Util.number
库已经帮我们实现好了,直接调用即可 - 遍历字符串,每一位的ascii值与A的ascii值之差(假设全为大写)再减去偏移量b,最后的结果再乘a的逆元,模26,得到对应的字母在字母表的序号(比如说 0 对应 A,1 对应 B 这样)
- 序号加上A的ascii值,再把每个得到的字符拼接在一起即可
以书上的第二题为例,对应的解密代码:
from Crypto.Util.number import *
def decrypt(enc,a,b):
dec = ""
re_a=inverse(a,26)
for char in enc:
temp=(ord(char)-ord("A")-b)*re_a %26
dec+=chr(temp+ord("A"))
return dec
prefix = "IF"
enc = "EDSGICKXHUKLZVEQZVKXWKZUKVCUH"
def cal():
for a in range(26):
for b in range(26):
m1 = ord('I') - ord('A')
m2 = ord('F') - ord('A')
c1 = ord('E') - ord('A')
c2 = ord('D') - ord('A')
if ((a*m1 + b) % 26 == c1) and ((a*m2 + b)%26 == c2):
return a,b
a,b = cal()
print(decrypt(enc,a,b))
多表代换
Hill
希尔密码(Hill)使用每个字母在字母表中的顺序作为其对应的数字,即 A=0,B=1,C=2 等,然后将明文转化为 n 维向量,跟一个 n × n 的矩阵相乘,再将得出的结果模 26
注意用作加密的矩阵(即密匙)必须是可逆的
在线工具:www.practicalcryptography.com/ciphers/hill-cipher/
对应的数学公式:
Ci ≡ AMi + B(mod M),i = 1,2, … ,j
计算流程:
后面几位的求密文的步骤也是一样的
解密时,只需要求A的逆矩阵再和每一组矩阵相乘即可得到明文
Vigenere
维吉尼亚密码(Vigenère cipher)是一种多表代换密码,也称为多重代换密码
维吉尼亚密码的加密过程是将明文按照关键词(密钥)重复排列,然后用不同的凯撒密码表对每个字符进行加密。每个凯撒密码表是由26个字母构成的,其中每个字母按照固定的偏移量进行置换。关键词的长度可以与明文的长度不同。解密时,需要使用相同的关键词和偏移量,依次对密文中的每个字符进行解密。
加密
流程:以下面这个为例
明文:come greatwall
密钥:crypto
首先,对密钥进行填充使其长度与明文长度一样
明文 c o m e g r e a t w a l l 密钥 c r y p t o c r y p t o c 接下来查表得到密文
最终得到密文:
明文:come greatwall
密钥:crypto
密文:efkt zferrltzn
解密
破译维吉尼亚密码的关键在于它的密钥是循环重复的
基于类似 the 这样的常用单词有可能被同样的密钥字母进行加密,从而在密文中重复出现
例如,明文中不同的CRYPTO
可能被密钥ABCDEF
加密成不同的密文:
密钥:ABCDEF AB CDEFA BCD EFABCDEFABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASXT IT UKSWT GQU GWYQVRKWAQJB
此时明文中重复的元素在密文中并不重复。然而,如果密钥相同的话,结果可能便为(使用密钥ABCD
):
密钥:ABCDAB CD ABCDA BCD ABCDABCDABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASTP KV SIQUT GQU CSASTPIUAQJB
那么我们就可以爆破最终得到密钥长度
在线解密网站:Vigenere Solver | guballa.de
脚本
来源于nss上某位大佬的脚本
letter_list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # 字母表
# 根据输入的key生成key列表
def Get_KeyList(key):
key_list = []
for ch in key:
key_list.append(ord(ch.upper()) - 65)
return key_list
# 加密函数
def Encrypt(plaintext, key_list):
ciphertext = ""
i = 0
for ch in plaintext: # 遍历明文
if 0 == i % len(key_list):
i = 0
if ch.isalpha(): # 明文是否为字母,如果是,则判断大小写,分别进行加密
if ch.isupper():
ciphertext += letter_list[(ord(ch) - 65 + key_list[i]) % 26]
i += 1
else:
ciphertext += letter_list[(ord(ch) - 97 + key_list[i]) %
26].lower()
i += 1
else: # 如果密文不为字母,直接添加到密文字符串里
ciphertext += ch
return ciphertext
# 解密函数
def Decrypt(ciphertext, key):
plaintext = ""
i = 0
for ch in ciphertext: # 遍历密文
if 0 == i % len(key_list):
i = 0
if ch.isalpha(): # 密文为否为字母,如果是,则判断大小写,分别进行解密
if ch.isupper():
plaintext += letter_list[(ord(ch) - 65 - key_list[i]) % 26]
i += 1
else:
plaintext += letter_list[(ord(ch) - 97 - key_list[i]) %
26].lower()
i += 1
else: # 如果密文不为字母,直接添加到明文字符串里
plaintext += ch
return plaintext
if __name__ == '__main__':
print("加密请按D,解密请按E:")
user_input = input()
while (user_input != 'D' and user_input != 'E'): # 输入合法性判断
print("输入有误!请重新输入:")
user_input = input()
print("请输入密钥,随便输后面会爆破,大写字母就行:")
key = input()
while (False == key.isalpha()): # 输入合法性判断
print("输入有误!密钥为字母,请重新输入:")
key = input()
key_list = Get_KeyList(key)
# print(key_list)
if user_input == 'D':
# 加密
print("请输入明文:")
plaintext = input()
ciphertext = Encrypt(plaintext, key_list)
print("密文为:\n%s" % ciphertext)
else:
# 解密
print(key_list)
print("请输入密文,把密文输进去:")
ciphertext = input()
for i in range(26):
for j in range(26):
for k in range(26):
key_list = [i, j, k]
plaintext = Decrypt(ciphertext, key_list)
if "NSS" in plaintext:
print(plaintext)
break
# print("明文为:\n%s" % plaintext)