1. 项目概述:为什么我们要亲手复现经典密码?
在信息安全领域,密码学是基石。但很多初学者一上来就接触AES、RSA这些现代密码算法,常常感觉云里雾里,知其然不知其所以然。这就好比还没学会走路就想跑。我干了十多年安全开发和教学,发现一个规律: 真正理解密码学精髓的捷径,是从它的历史源头开始,亲手把那些经典算法“敲”出来。
这次,我们就用Python这把“瑞士军刀”,从最古老的凯撒密码出发,一路走到相对复杂的维吉尼亚密码,完整复现5种在密码学发展史上具有里程碑意义的经典密码。这不仅仅是写几行代码,更是一次穿越密码学历史的思维训练。你会亲眼看到,密码设计者是如何在“加密强度”和“实现简易性”之间反复权衡、迭代创新的。通过复现,你能深刻理解“唯密文攻击”、“已知明文攻击”这些概念到底意味着什么,以及现代密码算法中的“混淆”和“扩散”原则是如何萌芽的。
对于正在学习Python的你,这也是绝佳的练手项目。它涵盖了字符串处理、列表推导式、模运算、字典映射、函数封装等多个核心知识点,而且每个算法代码量不大,成就感来得很快。无论你是想入门网络安全、参加CTF比赛,还是单纯对逻辑和编程感兴趣,这个项目都能让你在动手实践中,把抽象的理论变成指尖下实实在在的、可以运行的“历史”。
2. 核心思路与算法选型:一条清晰的演进脉络
在动手写代码之前,我们必须先理清思路:为什么选这五种密码?它们之间有什么内在联系?我的设计思路是遵循历史发展和技术演进的逻辑,搭建一条从简单替换到多表替代的清晰学习路径。这样你学到的不是五个孤立的算法,而是一套完整的知识体系。
2.1 算法演进图谱:从单表替换到多表替代
我选择的五种密码,完美勾勒了古典密码学的一条核心进化线:
-
凯撒密码 (Caesar Cipher) : 奠基之作 。所有替换密码的鼻祖。核心思想是“移位”,将明文中的每个字母在字母表上向后(或向前)移动一个固定数目。它引入了“密钥”的最原始概念(那个固定的移动位数),但因其过于简单,几乎没有任何实战价值,是理解“单表替换”的绝佳起点。
-
仿射密码 (Affine Cipher) : 凯撒的数学升级版 。它在凯撒密码“加法移位”的基础上,引入了“乘法”运算。加密过程变为
(a * x + b) mod 26,其中a和b是密钥。这带来了两个关键进步:一是加密规则因为乘法因子a而变得更加复杂;二是它对密钥a提出了要求(必须与26互质),这隐约体现了现代密码学中“密钥空间”和“算法约束”的思想。 -
简单替换密码 (Simple Substitution Cipher) : 单表替换的终极形态 。它不再使用固定的、有规律的移位规则,而是使用一个完全随机的、打乱的字母表作为密钥。比如A对应Q,B对应F,C对应M……理论上,它的密钥空间巨大(26!),暴力破解几乎不可能。但它依然脆弱,因为它保留了原始语言(如英语)的统计特征(字母频率、单词模式),通过频率分析可以轻松击破。这让我们深刻认识到, 仅仅依靠巨大的密钥空间并不足以保证安全 。
-
维吉尼亚密码 (Vigenère Cipher) : 革命性的突破 。它首次引入了“多表替换”的概念。使用一个关键词(如“KEY”)作为密钥,明文中不同位置的字母,会根据密钥字母的不同,采用不同的凯撒移位表进行加密。这有效破坏了单表替换密码中明文统计特征的暴露,在数百年内被认为是“不可破译”的。理解维吉尼亚密码,是理解现代流密码(如RC4)和分组密码工作模式(如CFB)的思想雏形。
-
栅栏密码 (Rail Fence Cipher) : 换位密码的代表 。它跳出了“替换”的范畴,展示了密码学的另一大分支——换位(Transposition)。其核心是改变明文字母的排列顺序,而不改变字母本身。栅栏密码将明文写成锯齿形(如“之”字形),再按行读取形成密文。虽然本身非常脆弱,但它揭示了“扩散”原则的早期形式:将明文中的一个比特的影响扩散到密文的多个位置。
注意 :这个顺序是精心设计的。前三种是“替换密码”,让你逐步感受加密规则复杂化的过程;维吉尼亚是“多表替换”,是质的飞跃;栅栏密码则切换到“换位”维度,拓宽视野。按此顺序实现,理解是层层递进的。
2.2 为什么用Python实现?
Python几乎是实现这个项目的“天选之语”。原因有三:
- 表达力强 :字符串和列表操作极其方便,一行代码往往能完成C语言里一个循环的工作,让我们更专注于算法逻辑本身。
- 原型友好 :无需考虑复杂的内存管理和类型声明,可以快速实现想法并看到结果,学习反馈即时。
-
生态丰富
:虽然本项目用不到太多库,但Python在密码学(
cryptography)、数据分析(可用于频率分析)领域的强大生态,为后续深入学习铺平了道路。
3. 环境准备与基础工具函数
在开始逐个击破之前,我们需要搭建一个共用的“工作台”。主要是准备一个纯净的Python环境,并编写几个所有密码算法都会用到的辅助函数。
3.1 Python环境配置要点
如果你还没有Python环境,建议直接安装最新版的Python 3.x(如3.11或3.12)。从官网下载安装包时, 务必勾选“Add Python to PATH” ,这样可以在命令行全局调用Python。
安装后,打开终端(Windows用CMD或PowerShell,Mac/Linux用Terminal),输入
python --version
验证。我推荐使用VSCode作为代码编辑器,安装Python扩展后,写代码和调试都非常方便。
一个干净的虚拟环境不是必须的,但对于养成好习惯很重要。你可以在项目目录下运行:
python -m venv venv
# Windows激活: venv\Scripts\activate
# Mac/Linux激活: source venv/bin/activate
3.2 核心工具函数编写
几乎所有古典密码都只处理英文字母(A-Z)。因此,我们需要两个最基础的转换函数:将字符转换为0-25的数字,以及反向转换。
def char_to_num(c: str) -> int:
"""将大写字母转换为0-25的数字。"""
if not ('A' <= c <= 'Z'):
raise ValueError(f"字符 '{c}' 不是大写字母A-Z")
return ord(c) - ord('A')
def num_to_char(n: int) -> str:
"""将0-25的数字转换为大写字母。"""
if not (0 <= n < 26):
raise ValueError(f"数字 {n} 不在0-25范围内")
return chr(n + ord('A'))
另外,我们还需要一个预处理函数,用来清理输入的文本:去除空格、标点,并将所有字母统一为大写。这是古典密码的通用前提。
def preprocess_text(text: str) -> str:
"""预处理文本:只保留字母,并转换为大写。"""
return ''.join(filter(str.isalpha, text.upper()))
实操心得 :在函数开头添加
: str和-> int这样的类型注解(Type Hints)是非常好的习惯。它不会影响程序运行,但能让代码更易读,现代编辑器(如VSCode)也能据此提供更智能的提示和错误检查,极大提升开发效率。
4. 五种经典密码的Python复现详解
现在,让我们正式进入密码的世界,从简单到复杂,逐一实现并剖析。
4.1 凯撒密码:一切的开始
凯撒密码的原理简单到令人发指:每个字母向后移动固定的位置(比如3)。Z之后循环回到A。
加密公式
:
C = (P + K) mod 26
解密公式
:
P = (C - K) mod 26
其中,P是明文数字,C是密文数字,K是移位量(密钥)。
Python实现 :
def caesar_encrypt(plaintext: str, shift: int) -> str:
"""凯撒密码加密。"""
plaintext = preprocess_text(plaintext)
ciphertext_chars = []
for char in plaintext:
p_num = char_to_num(char)
c_num = (p_num + shift) % 26
ciphertext_chars.append(num_to_char(c_num))
return ''.join(ciphertext_chars)
def caesar_decrypt(ciphertext: str, shift: int) -> str:
"""凯撒密码解密。"""
ciphertext = preprocess_text(ciphertext)
plaintext_chars = []
for char in ciphertext:
c_num = char_to_num(char)
p_num = (c_num - shift) % 26
plaintext_chars.append(num_to_char(p_num))
return ''.join(plaintext_chars)
# 示例
plaintext = "HELLO WORLD"
key = 3
cipher = caesar_encrypt(plaintext, key) # 得到“KHOOR ZRUOG”
decrypted = caesar_decrypt(cipher, key) # 恢复“HELLOWORLD”
为什么模26(mod 26)?
这是实现“循环移位”的关键。当
p_num + shift
的结果超过25(即字母‘Z’)时,
% 26
操作会自动将其“折回”到字母表开头。例如,‘Z’(25) + 3 = 28, 28 % 26 = 2,即‘C’。
注意事项 :凯撒密码的密钥空间只有25种可能(移位1到25,移位0等于没加密)。一个简单的暴力破解程序,尝试所有25种移位,就能瞬间破解。在复现时,你可以顺手写一个
caesar_brute_force(ciphertext)函数,打印所有可能结果,肉眼就能找出有意义的明文。这直观地展示了什么叫“唯密文攻击”以及密钥空间过小的危害。
4.2 仿射密码:引入数学复杂度
仿射密码可以看作凯撒密码的“Pro Max”版。加密时,先对明文数字做乘法,再加一个偏移量。
加密公式
:
C = (a * P + b) mod 26
解密公式
:
P = a_inv * (C - b) mod 26
其中,
a
和
b
是密钥。
a
必须与26互质(即最大公约数gcd(a, 26) = 1),否则解密时无法找到乘法逆元
a_inv
,会导致多个明文对应同一个密文,无法唯一解密。
Python实现 :
def gcd(a: int, b: int) -> int:
"""求最大公约数,用于检查密钥a是否合法。"""
while b:
a, b = b, a % b
return a
def mod_inverse(a: int, m: int) -> int:
"""求模m下的乘法逆元。即找到 a_inv 使得 (a * a_inv) % m == 1。"""
for x in range(1, m):
if (a * x) % m == 1:
return x
raise ValueError(f"{a}在模{m}下没有乘法逆元")
def affine_encrypt(plaintext: str, a: int, b: int) -> str:
"""仿射密码加密。"""
if gcd(a, 26) != 1:
raise ValueError(f"密钥a={a}必须与26互质")
plaintext = preprocess_text(plaintext)
ciphertext_chars = []
for char in plaintext:
p_num = char_to_num(char)
c_num = (a * p_num + b) % 26
ciphertext_chars.append(num_to_char(c_num))
return ''.join(ciphertext_chars)
def affine_decrypt(ciphertext: str, a: int, b: int) -> str:
"""仿射密码解密。"""
if gcd(a, 26) != 1:
raise ValueError(f"密钥a={a}必须与26互质")
a_inv = mod_inverse(a, 26)
ciphertext = preprocess_text(ciphertext)
plaintext_chars = []
for char in ciphertext:
c_num = char_to_num(char)
p_num = (a_inv * (c_num - b)) % 26
plaintext_chars.append(num_to_char(p_num))
return ''.join(plaintext_chars)
# 示例
plaintext = "AFFINE CIPHER"
a, b = 5, 8 # 5和26互质
cipher = affine_encrypt(plaintext, a, b) # 得到“IHHWVCSWFRCP”
decrypted = affine_decrypt(cipher, a, b) # 恢复“AFFINECIPHER”
核心难点:乘法逆元
。这是仿射密码的“钥匙扣”。在常规算术里,5的倒数是0.2。但在模26的世界里,我们要找一个整数
a_inv
,使得
(a * a_inv) % 26 == 1
。对于
a=5
,我们可以试出来
5*21=105
,
105 % 26 = 1
,所以
a_inv = 21
。解密公式
a_inv * (C - b) mod 26
其实就是加密公式的逆运算。
实操心得 :
mod_inverse函数使用了最直观的遍历法,因为26很小。在实际密码学中,模数(如RSA中的n)巨大,需要使用扩展欧几里得算法来高效计算逆元。这里为了清晰理解概念,遍历法更合适。你可以思考:与26互质的a有哪些?(1,3,5,7,9,11,15,17,19,21,23,25),所以仿射密码的密钥空间是12*26=312种,虽然比凯撒多,但仍然很小。
4.3 简单替换密码:密钥空间的飞跃与频率分析的脆弱
简单替换密码使用一个完全打乱的字母表作为密钥。例如,密钥是“QWERTYUIOPASDFGHJKLZXCVBNM”,那就意味着A->Q, B->W, C->E, ... , Z->M。
Python实现 :
import random
def generate_random_key() -> str:
"""生成一个随机的简单替换密码密钥(打乱的字母表)。"""
alphabet = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
random.shuffle(alphabet)
return ''.join(alphabet)
def simple_substitution_encrypt(plaintext: str, key: str) -> str:
"""简单替换密码加密。"""
plaintext = preprocess_text(plaintext)
# 构建加密映射字典:原始字母 -> 密钥字母
encrypt_map = {}
for i in range(26):
encrypt_map[chr(ord('A') + i)] = key[i]
# 逐字符替换
ciphertext_chars = [encrypt_map[ch] for ch in plaintext]
return ''.join(ciphertext_chars)
def simple_substitution_decrypt(ciphertext: str, key: str) -> str:
"""简单替换密码解密。"""
ciphertext = preprocess_text(ciphertext)
# 构建解密映射字典:密钥字母 -> 原始字母
decrypt_map = {}
for i in range(26):
decrypt_map[key[i]] = chr(ord('A') + i)
# 逐字符替换
plaintext_chars = [decrypt_map[ch] for ch in ciphertext]
return ''.join(plaintext_chars)
# 示例
plaintext = "THIS IS A SECRET MESSAGE"
random_key = generate_random_key() # 例如 “XQAKZOSNJWYVBMCPLURDTEFGHI”
cipher = simple_substitution_encrypt(plaintext, random_key)
decrypted = simple_substitution_decrypt(cipher, random_key)
它的强大与脆弱 :它的密钥空间是26!(约4e26),这是一个天文数字,暴力破解在理论上不可行。然而,它有一个致命伤: 它保留了原始语言的统计特征 。在英文中,字母E的出现频率远高于Z。在密文里,出现频率最高的那个字母,很可能就对应明文的E。通过分析双字母组合(如TH, HE)、三字母组合(THE, AND),甚至单词结构,攻击者可以在不知道密钥的情况下,相对轻松地还原出大部分映射关系,进而破解密文。这种方法叫做 频率分析 。
注意事项 :在实现时,使用字典(
dict)来存储加密/解密映射是最高效的方式,查询时间复杂度是O(1)。你可以尝试写一个频率分析函数,输入一大段密文,统计各字母出现次数并排序,然后与英文字母标准频率表对比,体验一下“破解”的乐趣。这能让你刻骨铭心地理解,为什么现代密码学强调“混淆”,要让密文的统计特征尽可能均匀、随机。
4.4 维吉尼亚密码:多表替换的里程碑
维吉尼亚密码是古典密码学的巅峰之作。它使用一个关键词(如“KEY”)作为密钥。加密时,将明文和密钥重复对齐,用密钥字母决定对明文字母使用哪一张凯撒移位表。
加密过程
:
明文: ATTACKATDAWN
密钥: KEYKEYKEYKEY (重复使用)
加密: 对于第i个字符,移位量 =
char_to_num(KEY[i])
。
A(0) + K(10) = K
T(19) + E(4) = X
T(19) + Y(24) = R
... 以此类推。
密文: KXVRP MQFFU
Python实现 :
def vigenere_encrypt(plaintext: str, keyword: str) -> str:
"""维吉尼亚密码加密。"""
plaintext = preprocess_text(plaintext)
keyword = preprocess_text(keyword)
if not keyword:
raise ValueError("密钥不能为空")
ciphertext_chars = []
key_length = len(keyword)
# 将密钥转换为数字列表,避免在循环中重复计算
key_shifts = [char_to_num(k) for k in keyword]
for i, char in enumerate(plaintext):
p_num = char_to_num(char)
shift = key_shifts[i % key_length] # 循环使用密钥
c_num = (p_num + shift) % 26
ciphertext_chars.append(num_to_char(c_num))
return ''.join(ciphertext_chars)
def vigenere_decrypt(ciphertext: str, keyword: str) -> str:
"""维吉尼亚密码解密。"""
ciphertext = preprocess_text(ciphertext)
keyword = preprocess_text(keyword)
if not keyword:
raise ValueError("密钥不能为空")
plaintext_chars = []
key_length = len(keyword)
key_shifts = [char_to_num(k) for k in keyword]
for i, char in enumerate(ciphertext):
c_num = char_to_num(char)
shift = key_shifts[i % key_length]
p_num = (c_num - shift) % 26
plaintext_chars.append(num_to_char(p_num))
return ''.join(plaintext_chars)
# 示例
plaintext = "ATTACK AT DAWN"
keyword = "KEY"
cipher = vigenere_encrypt(plaintext, keyword) # 得到“KXVRPMQFFU”
decrypted = vigenere_decrypt(cipher, keyword) # 恢复“ATTACKATDAWN”
为什么它更安全? 因为它破坏了单表替换的统计特征。明文中同一个字母(比如多个‘A’),如果对应的密钥字母不同,会被加密成不同的密文字母。这极大地增加了频率分析的难度。密钥越长,重复周期越长,密文就越难被分析。
破解思路(卡西斯基试验) :尽管维吉尼亚密码很强,但它并非无懈可击。如果密钥较短,密文中可能会出现重复的片段,这些片段之间的距离很可能就是密钥长度的倍数。通过寻找重复片段、推测密钥长度,可以将密文按密钥长度分组,每组就变成了一个简单的单表替换密码(因为组内使用的移位是固定的),然后再用频率分析分别破解。这个过程虽然繁琐,但给出了破解多表密码的系统性方法。
实操心得 :在代码中,
i % key_length是实现密钥循环使用的精髓。你可以尝试用很短的密钥(如“A”或“AB”)加密一段长文本,观察密文,再换一个很长的密钥(如一个长句子)加密同一段文本,对比两者密文的“随机感”,直观感受密钥长度对安全性的影响。
4.5 栅栏密码:换位密码的直观体验
栅栏密码不改变字母本身,只改变它们的位置。以栏数(高度)为2为例: 明文: HELLOWORLD 写成两行(栅栏): H L O O L E L W R D 然后按行读取密文:HLOOL ELWRD(通常合并为“HLOOLELWRD”)。
Python实现(加密) :
def rail_fence_encrypt(plaintext: str, rails: int) -> str:
"""栅栏密码加密。"""
plaintext = preprocess_text(plaintext)
if rails <= 1:
return plaintext
# 创建rails个空字符串,代表每一行(栏)
fence = [''] * rails
rail = 0
direction = 1 # 1表示向下,-1表示向上
for char in plaintext:
fence[rail] += char
rail += direction
# 到达顶部或底部时转向
if rail == 0 or rail == rails - 1:
direction = -direction
return ''.join(fence)
def rail_fence_decrypt(ciphertext: str, rails: int) -> str:
"""栅栏密码解密。"""
ciphertext = preprocess_text(ciphertext)
if rails <= 1:
return ciphertext
# 第一步:模拟加密过程,计算出每一栏应有的长度
fence = [''] * rails
rail_lengths = [0] * rails
rail = 0
direction = 1
for _ in ciphertext:
rail_lengths[rail] += 1
rail += direction
if rail == 0 or rail == rails - 1:
direction = -direction
# 第二步:根据计算出的长度,将密文分割到各栏
index = 0
for i in range(rails):
fence[i] = ciphertext[index:index + rail_lengths[i]]
index += rail_lengths[i]
# 第三步:模拟读取过程,重建明文
result = []
rail = 0
direction = 1
rail_positions = [0] * rails # 记录每一栏当前读取到的位置
for _ in range(len(ciphertext)):
result.append(fence[rail][rail_positions[rail]])
rail_positions[rail] += 1
rail += direction
if rail == 0 or rail == rails - 1:
direction = -direction
return ''.join(result)
# 示例
plaintext = "HELLOWORLD"
rails = 3
cipher = rail_fence_encrypt(plaintext, rails) # 例如 rails=3 时得到 “HOLELWRDLO”
decrypted = rail_fence_decrypt(cipher, rails) # 恢复“HELLOWORLD”
解密算法为什么更复杂? 因为加密时我们是按顺序把字符“放”到栅栏里,解密时我们需要知道每个栅栏里“放”了多少字符,才能正确地把密文“取”出来。所以解密分三步:1. 模拟加密走一遍,算出每栏长度;2. 按长度分割密文;3. 再模拟加密的“之字形”路径,从各栏依次取字符,拼回明文。
注意事项 :栅栏密码的安全性极低,栏数稍微一多,人眼就能看出规律,更别说计算机了。复现它的主要价值在于理解“换位”这个概念,以及体验如何逆向一个过程(写解密算法)。你可以尝试修改代码,实现从第一栏开始先向下再向上的“W型”栅栏,或者尝试不同的读取顺序(如从左下角开始斜着读),感受换位密码的变体。
5. 综合测试与算法对比分析
代码写完了,不能只跑通示例就完事。我们需要设计更全面的测试用例,并横向对比这些算法的特点,这样才能形成系统性的认知。
5.1 设计全面的测试用例
一个好的测试应该覆盖边界情况和异常输入。
def test_all_ciphers():
"""综合测试所有密码算法。"""
test_strings = [
"HELLO", # 普通短句
"A", # 单字符边界
"Z", # 字母表边界
"HELLO WORLD!", # 包含空格标点
"Hello World", # 大小写混合
"", # 空字符串(需处理)
"123", # 无字母字符串
]
print("=== 凯撒密码测试 ===")
for text in test_strings:
try:
encrypted = caesar_encrypt(text, 5)
decrypted = caesar_decrypt(encrypted, 5)
print(f"原文: '{text}' -> 密文: '{encrypted}' -> 解密: '{decrypted}' (匹配: {text.upper() == decrypted})")
except Exception as e:
print(f"原文: '{text}' -> 错误: {e}")
print("\n=== 维吉尼亚密码测试 (密钥'KEY') ===")
for text in test_strings:
try:
encrypted = vigenere_encrypt(text, "KEY")
decrypted = vigenere_decrypt(encrypted, "KEY")
print(f"原文: '{text}' -> 密文: '{encrypted}' -> 解密: '{decrypted}'")
except Exception as e:
print(f"原文: '{text}' -> 错误: {e}")
# 运行测试
if __name__ == "__main__":
test_all_ciphers()
5.2 五种密码特性对比表
通过实现,我们可以从多个维度对比这些经典密码:
| 特性维度 | 凯撒密码 | 仿射密码 | 简单替换密码 | 维吉尼亚密码 | 栅栏密码 |
|---|---|---|---|---|---|
| 密码类型 | 单表替换 | 单表替换 | 单表替换 | 多表替换 | 换位 |
| 核心操作 | 加法移位 | 乘加运算 | 随机映射 | 循环密钥移位 | 位置重排 |
| 密钥形式 | 整数 (0-25) | 一对整数(a,b) | 乱序字母表 | 关键词/短语 | 栏数(整数) |
| 密钥空间 | 极小 (25) | 小 (312) | 极大 (26!) | 随密钥长度指数增长 | 小 (文本长度内) |
| 抗频率分析 | 极弱 | 极弱 | 弱 (保留统计特征) | 较强 (破坏统计特征) | 弱(保留字母本身) |
| 抗暴力破解 | 极弱 | 弱 | 理论上极强 | 依赖密钥长度 | 极弱 |
| 历史意义 | 起源,概念启蒙 | 引入数学运算 | 展示密钥空间与安全的非正比关系 | 多表替换开创者,长期被认为不可破 | 换位密码代表 |
| 现代启示 | 理解“移位”和模运算 | 理解“逆元”和算法约束 | 理解“混淆”不足的危害 | 理解“密钥流”和“周期”的概念 | 理解“扩散”的雏形 |
从这个表可以清晰看出密码设计的演进逻辑:从 固定规则 (凯撒)到 参数化规则 (仿射),再到 随机化规则 (简单替换)。当单表替换因频率分析而失败后,密码学家转向了 动态变化规则 (维吉尼亚)。而栅栏密码则代表了另一条并行思路—— 改变结构 。
6. 常见问题与实战技巧
在实际复现和后续探索中,你肯定会遇到一些问题。这里我总结几个最常见的,并分享一些从实战中得来的技巧。
6.1 复现过程中的典型问题与解决
-
解密结果不对,多了或少了一些字符?
-
检查点1:预处理函数
。确保加密和解密都使用了相同的
preprocess_text函数。一个常见的错误是加密时处理了空格,解密时没处理,导致索引对不上。 -
检查点2:模运算
。Python的
%运算符对负数取模的结果是正数(如-1 % 26 = 25),这符合我们的需求。但如果你自己实现了取模函数,要确保逻辑一致。 -
检查点3:密钥一致性
。对于仿射密码,确保加密和解密使用的
a,b相同,且a是合法的。对于维吉尼亚密码,确保关键词一模一样(大小写、顺序)。
-
检查点1:预处理函数
。确保加密和解密都使用了相同的
-
仿射密码解密时提示“没有乘法逆元”?
-
这绝对是密钥
a选错了。a必须与26互质。快速验证:gcd(a, 26)必须等于1。记住那些合法的a: 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25。
-
这绝对是密钥
-
维吉尼亚密码的密文看起来还是有规律?
- 如果你的密钥很短(比如“A”或“AB”),那么密文的周期性会非常明显,肉眼可能看出重复模式。尝试使用一个长度超过20个字符的、无重复的密钥短语来加密,再看密文,它会看起来像一堆完全随机的字母。这直观展示了 密钥长度是维吉尼亚密码安全性的生命线 。
6.2 扩展挑战与进阶思路
当你完美复现了以上五种密码后,可以尝试以下挑战,让理解更上一层楼:
-
挑战1:实现“自动破解”凯撒密码 。 写一个函数
caesar_cracker(ciphertext),它尝试所有25种移位,并利用英文单词频率(或一个常用词字典)自动选择最可能是明文的结果。这让你初步体验自动化密码分析。 -
挑战2:为简单替换密码编写频率分析辅助工具 。 输入一段足够长的密文,程序统计字母频率、双字母频率,并列出最可能的映射。你可以半自动地完成破解。
-
挑战3:实现维吉尼亚密码的“卡西斯基试验”第一步 。 编写函数寻找密文中重复出现的3-4字母片段,并计算它们之间的距离。这些距离的最大公约数,很可能就是密钥长度。这是破解维吉尼亚密码的关键第一步。
-
挑战4:将算法封装成类 。 为每种密码定义一个Python类(如
class CaesarCipher:),将密钥作为初始化参数,encrypt和decrypt作为方法。这更符合现代编程习惯,也便于管理状态。 -
挑战5:支持扩展字符集 。 目前的算法只支持A-Z。尝试修改代码,使其支持大小写、数字、甚至空格和标点(通常空格和标点保留不变)。这会大大增加代码的实用性,也更贴近真实场景。
我个人最推荐的是挑战1和挑战3 。它们能让你从“密码使用者”转变为“密码分析者”,这种视角的转换对理解密码学的攻防本质至关重要。当你成功写出一个能自动破解短凯撒密文的脚本时,那种成就感会让你彻底明白,为什么这些古典密码会退出历史舞台。
最后,别忘了,我们复现这些“过时”的密码,目的不是为了使用它们,而是为了 理解现代密码学大厦是如何从这些基石上建立起来的 。AES的S盒带来了比简单替换更强大的混淆;RSA的非对称性源于数论,其思想比仿射密码深邃万倍;而工作模式则继承了多表替换的思想,确保相同的明文块产生不同的密文块。亲手搭建过这些古老的砖石,你再去看现代密码学的宏伟建筑,眼光自然会不一样。

366

被折叠的 条评论
为什么被折叠?



