1.凯撒密码(Caesar Cipher)

凯撒密码是最经典的单字母移位替换密码,也是密码学入门的标志性算法。其核心思想是将明文中的每一个字母,按照字母表顺序统一向前或向后移动固定位数,从而形成密文。

1.1加密原理

对于每个明文字母 m,按密钥 k(位移量)进行移位:$ c = (m + k) mod 26$ 例如密钥为 3 时,A→D、B→E、X→A,以此类推。

1.2解密原理

将密文字母反向移动相同位数即可恢复明文: [ m = (c - k) \mod 26 ] 凯撒密码实现简单、运算高效,但安全性极低,仅能抵御简单的信息偷窥,通过频率分析或暴力枚举 25 种可能即可快速破解。它更多用于教学,帮助理解“替换密码”的基本思想。

1.3加解密代码

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
def caesar_cipher(text, shift, mode='encrypt'):
"""凯撒密码加解密"""
# 解密 = 反向偏移
if mode == 'decrypt':
shift = -shift

result = ""
for char in text:
if char.isupper():
shifted = (ord(char) - ord('A') + shift) % 26
result += chr(shifted + ord('A'))
elif char.islower():
shifted = (ord(char) - ord('a') + shift) % 26
result += chr(shifted + ord('a'))
else:
result += char # 符号数字不变
return result


def brute_force_caesar(cipher_text):
"""
暴力破解
尝试所有 26 种偏移,输出全部结果
"""
print("\n===== 暴力破解 =====\n")
for shift in range(26):
plain = caesar_cipher(cipher_text, shift, mode='decrypt')
print(f"偏移量 {shift:2d}{plain}")


# ===================== 攻击 =====================
if __name__ == "__main__":
# 你要破解的密文
cipher = input("请输入密文:")

print("【待破解密文】:", cipher)

# 暴力破解
brute_force_caesar(cipher)

2.斯巴达密码(Scytale)

斯巴达密码是古希腊军队使用的古典换位密码,它不替换字母,只改变字符顺序,依靠一根圆柱形密码棒完成加解密。

2.1加密原理

(1)将长条羊皮纸螺旋状紧密缠绕在密码棒上

(2)横向逐行书写明文

(3)解开羊皮纸,字符沿斜向打乱,形成无意义密文。

本质上是按固定列数对明文进行矩阵排列,再按行读取。设密码棒周长对应列数为 k,明文按 k 列填充矩阵,密文为按列读取结果。

2.2解密原理

使用相同直径的密码棒重新缠绕密文纸条,字符会自动恢复为原来的行列顺序,直接读取即可得到明文。 斯巴达密码依靠“扰乱位置”实现保密,结构简单、适合战场快速使用,但抵抗密码分析能力较弱,属于典型的换位密码范例。

2.3加解密代码

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
def scytale_encrypt(text, d):
"""斯巴达密码加密"""
text = text.replace(" ", "")
rows = (len(text) + d - 1) // d # 计算行数(向上取整)
grid = [['' for _ in range(d)] for _ in range(rows)]
idx = 0
# 按 列 填入
for col in range(d):
for row in range(rows):
if idx < len(text):
grid[row][col] = text[idx]
idx += 1
# 按 行 读取
return ''.join(''.join(row) for row in grid)


def scytale_decrypt(cipher, d):
"""斯巴达密码解密"""
n = len(cipher)
rows = (n + d - 1) // d
grid = [['' for _ in range(d)] for _ in range(rows)]
idx = 0

# 按行填入
for row in range(rows):
for col in range(d):
if idx < n:
grid[row][col] = cipher[idx]
idx += 1

# 按列读取
result = []
for col in range(d):
for row in range(rows):
if grid[row][col] != '':
result.append(grid[row][col])
return ''.join(result)


def brute_force_scytale(cipher_text):
"""
斯巴达密码 暴力破解
尝试所有可能的 d(列数)
"""
print("\n===== 斯巴达密码 暴力破解结果 =====")
n = len(cipher_text)

# 尝试所有可能的密钥 d:1 ~ 密文长度
for d in range(1, n + 1):
try:
plain = scytale_decrypt(cipher_text, d)
print(f"密钥 d={d:2d}{plain}")
except:
continue


# ==================== 测试 ====================
if __name__ == "__main__":
# 1. 加密
plaintext = input("请输入要加密的信息:")
key_d = int(input("请输入密钥:")) #密钥:列数
cipher = scytale_encrypt(plaintext, key_d)
print(f"\n加密结果(密文): {cipher}")

# 2. 正常解密(知道密钥时)
decrypted = scytale_decrypt(cipher, key_d)
print(f"正常解密(密钥={key_d}): {decrypted}")

# 3. 攻击者不知道密钥 → 暴力破解
print("\n==== 攻击者截获密文,开始暴力破解 ====")
brute_force_scytale(cipher)

3.Playfair 密码

Playfair 密码是 19 世纪出现的经典双字母替换密码,曾在一战、二战中被军方大量使用,安全性远高于单字母替换密码。

3.1加密原理

1.根据密钥构建一个 5×5 字母矩阵(通常去掉字母 I/J 合并)

2.将明文两两分组,若出现重复字母则插入填充字母(如 X),长度为奇数则末尾补 X;

3.对每一对字母按矩阵位置规则替换:

  • 同一行:各自向右循环移一位;
  • 同一列:各自向下循环移一位;
  • 形成矩形:以对角位置字母替换,保持行不变。

3.2解密原理

使用相同密钥矩阵,执行加密的逆操作:同行左移、同列上移、矩形对角反向替换,逐对还原即可得到明文。 Playfair 密码以双字母为单元加密,大幅降低了频率分析的有效性,在古典密码中具有里程碑意义,也是分组密码思想的早期雏形。

3.3加解密代码

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
class Playfair:#创建一个类
#同行右移,同列下移,矩形对角
def __init__(self, key):
# 生成矩阵(I/J合并)
s = []
for c in (key.upper().replace('J', 'I') + "ABCDEFGHIKLMNOPQRSTUVWXYZ"):
if c not in s:
s.append(c)
self.m = [s[i:i + 5] for i in range(0, 25, 5)] #生成步长为5的序列,得到5*5的矩阵
self.p = {c: (i // 5, i % 5) for i, c in enumerate(s)} #生成字典,字母与坐标配对

def _pair(self, a, b):
r1, c1 = self.p[a]
r2, c2 = self.p[b]
if r1 == r2: # 同行
return self.m[r1][(c1 + 1) % 5] + self.m[r2][(c2 + 1) % 5]
if c1 == c2: # 同列
return self.m[(r1 + 1) % 5][c1] + self.m[(r2 + 1) % 5][c2]
return self.m[r1][c2] + self.m[r2][c1] # 矩形

def encrypt(self, txt):
txt = ''.join(c for c in txt.upper().replace('J', 'I') if c.isalpha())
# 插入X处理重复和奇偶
i = 0
pairs = []
while i < len(txt):
a = txt[i]
b = txt[i + 1] if i + 1 < len(txt) else 'X'
if a == b:
pairs.append(a + 'X')
i += 1
else:
pairs.append(a + b)
i += 2
return ''.join(self._pair(a, b) for a, b in pairs)

def decrypt(self, txt):
txt = txt.upper().replace('J', 'I') #将输入转大写,同时因为i j合并,将j换成i
pairs = [txt[i:i + 2] for i in range(0, len(txt), 2)]
result = []
for a, b in pairs:
r1, c1 = self.p[a]
r2, c2 = self.p[b]
if r1 == r2:
result.append(self.m[r1][(c1 - 1) % 5] + self.m[r2][(c2 - 1) % 5])
elif c1 == c2:
result.append(self.m[(r1 - 1) % 5][c1] + self.m[(r2 - 1) % 5][c2])
else:
result.append(self.m[r1][c2] + self.m[r2][c1])
return ''.join(result).replace('X', '') # 简化处理


# 使用
key=input("请输入密钥:")
p = Playfair(key)
etext=input("请输入待加密文本:")
c = p.encrypt(etext)
print(f"密文: {c}")
dtext=input("请输入待解密文本:")
d = p.decrypt(dtext)
print(f"明文: {d}")