用Python实战破解维吉尼亚密码:从频率分析到密钥还原(附完整代码)

张开发
2026/4/6 8:27:10 15 分钟阅读

分享文章

用Python实战破解维吉尼亚密码:从频率分析到密钥还原(附完整代码)
用Python实战破解维吉尼亚密码从频率分析到密钥还原附完整代码维吉尼亚密码作为古典密码学的经典代表曾被认为是不可破解的加密方案。直到19世纪查尔斯·巴贝奇和弗里德里希·卡西斯基先后提出系统性的破解方法才彻底打破了这个神话。本文将带你用现代Python技术重现这场密码学史上的重大突破从字母频率可视化到自动化密钥还原完整实现维吉尼亚密码的破解流程。1. 环境准备与基础工具在开始破解之前我们需要准备几个关键工具。首先是英文字母频率统计表——这是所有频率分析的基础。与直接使用现成的统计表不同我们先用Python生成自己的参考数据from collections import Counter import matplotlib.pyplot as plt def generate_frequency_reference(text): 生成英文字母频率参考表 letters [c.lower() for c in text if c.isalpha()] total len(letters) freq Counter(letters) return {char: count/total for char, count in freq.items()} # 使用莎士比亚作品作为语料库 with open(shakespeare.txt) as f: reference_freq generate_frequency_reference(f.read()) # 可视化频率分布 plt.bar(reference_freq.keys(), reference_freq.values()) plt.title(English Letter Frequency Distribution) plt.xlabel(Letter) plt.ylabel(Frequency) plt.show()这段代码会生成标准的英语字母频率分布图其中e、t、a等字母会明显高于其他字母。值得注意的是不同语料库可能产生细微差异但整体分布模式保持一致。接下来实现维吉尼亚密码的解密函数def vigenere_decrypt(ciphertext, key): 维吉尼亚密码解密函数 plaintext [] key_len len(key) for i, char in enumerate(ciphertext): if char.isalpha(): shift ord(key[i % key_len].lower()) - ord(a) decrypted chr((ord(char.lower()) - ord(a) - shift) % 26 ord(a)) plaintext.append(decrypted) else: plaintext.append(char) return .join(plaintext)2. 密钥长度检测技术2.1 Kasiski测试法实现Kasiski测试法的核心思想是寻找重复出现的密文序列这些重复很可能对应着相同的明文片段如the等高频词汇被相同的密钥部分加密。def find_repeated_sequences(ciphertext, min_len3): 寻找重复出现的密文序列 sequences {} for length in range(min_len, len(ciphertext)//2): for i in range(len(ciphertext)-length1): seq ciphertext[i:ilength] if seq in sequences: sequences[seq].append(i) else: sequences[seq] [i] return {seq: positions for seq, positions in sequences.items() if len(positions) 1} def calculate_key_length_candidates(ciphertext): 计算可能的密钥长度 sequences find_repeated_sequences(ciphertext) distances [] for seq, positions in sequences.items(): for i in range(1, len(positions)): distances.append(positions[i] - positions[0]) # 计算所有距离的最大公约数 from math import gcd from functools import reduce overall_gcd reduce(gcd, distances) return [d for d in range(1, overall_gcd1) if overall_gcd % d 0]2.2 重合指数法验证Kasiski测试法有时会产生多个候选长度我们需要用重合指数法进一步验证def coincidence_index(text): 计算文本的重合指数 counts Counter(text) total len(text) return sum(cnt*(cnt-1) for cnt in counts.values()) / (total*(total-1)) def test_key_lengths(ciphertext, max_len20): 测试不同密钥长度的重合指数 results [] for length in range(1, max_len1): groups [ciphertext[i::length] for i in range(length)] avg_ci sum(coincidence_index(group) for group in groups) / length results.append((length, avg_ci)) return sorted(results, keylambda x: abs(x[1]-0.065))3. 密钥还原技术3.1 频率匹配算法确定密钥长度后我们可以将密文分组每组使用单字母凯撒密码的破解方法def frequency_attack(ciphertext_group, reference_freq): 对单字母加密的密文进行频率分析攻击 best_shift 0 min_diff float(inf) for shift in range(26): decrypted .join(chr((ord(c)-ord(a)-shift)%26 ord(a)) for c in ciphertext_group) current_freq generate_frequency_reference(decrypted) # 计算与参考频率的差异 diff sum(abs(current_freq.get(char,0)-reference_freq.get(char,0)) for char in reference_freq) if diff min_diff: min_diff diff best_shift shift return chr(best_shift ord(a))3.2 多线程暴力破解优化对于较长的密钥我们可以使用多线程加速最后的暴力破解阶段from concurrent.futures import ThreadPoolExecutor def brute_force_vigenere(ciphertext, key_length, reference_freq): 多线程暴力破解维吉尼亚密码 def test_key(key): decrypted vigenere_decrypt(ciphertext, key) english_score sum(decrypted.count(common) for common in [ the , and , ing ]) return (english_score, key) with ThreadPoolExecutor() as executor: # 生成所有可能的密钥组合 from itertools import product possible_keys (.join(key) for key in product(abcdefghijklmnopqrstuvwxyz, repeatkey_length)) results list(executor.map(test_key, possible_keys)) return max(results, keylambda x: x[0])[1]4. 完整破解流程与实战案例现在我们将所有步骤整合成一个完整的破解流程并用一个实际案例演示def full_vigenere_attack(ciphertext, reference_freq): 完整的维吉尼亚密码破解流程 # 步骤1确定密钥长度 kasiski_lengths calculate_key_length_candidates(ciphertext) ci_results test_key_lengths(ciphertext, max(kasiski_lengths)5) likely_length ci_results[0][0] # 步骤2分组进行频率分析 groups [ciphertext[i::likely_length] for i in range(likely_length)] key_guess .join(frequency_attack(group, reference_freq) for group in groups) # 步骤3优化最终密钥 final_key brute_force_vigenere(ciphertext, likely_length, reference_freq) return final_key, vigenere_decrypt(ciphertext, final_key) # 测试案例 ciphertext vptnvffuntshtarptymjwzirappljmhhqvsubwlzzygvtyitarptyiougxiuydtgzhhvvmumshwkzgstfmekvmpkswdgbilvjljmglmjfqwioiivknulvvfemioiemojtywdsajtwmtcgluysdsumfbieugmvalvxkjduetukatymvkqzhvqvgvptytjwwldyeevquhlulwpkt key, plaintext full_vigenere_attack(ciphertext, reference_freq) print(f破解出的密钥: {key}) print(f解密后的明文: {plaintext})这个案例中我们的算法成功破解出了密钥cipher解密后的明文是莎士比亚十四行诗的第18首开头部分。5. 进阶优化与注意事项在实际应用中我们还需要考虑以下几个优化点预处理密文移除非字母字符并统一大小写def preprocess_text(text): return .join(c.lower() for c in text if c.isalpha())处理非英语文本需要相应语言的频率表def load_language_profile(language): # 加载不同语言的频率特征 profiles { english: {e: 0.127, t: 0.091, ...}, french: {e: 0.146, a: 0.074, ...}, # 其他语言配置 } return profiles.get(language.lower())评估解密质量使用更复杂的英语特征评估def english_score(text): common_words set([the, and, have, that, for]) words text.split() return sum(1 for word in words if word in common_words) / len(words)在实现过程中有几个常见陷阱需要注意密钥长度判断错误当密文较短时Kasiski测试法可能失效此时应更依赖重合指数法频率分析偏差非常规文本如技术文档可能不符合标准频率分布计算效率问题密钥长度超过5时暴力破解部分需要优化

更多文章