资讯动态

Python破解替换密码:从频率分析到映射推断的完整实战

发布时间:2026/9/10 7:20:24 来源:尧图企业网站定制
最近有个朋友问我说在网上看到一段加密文字想用Python试试能不能还原出来。这类事情我捣鼓过不少今天就拿“替换密码”这个最经典的古典密码来做个完整拆解。替换密码Substitution Cipher本质上就是把明文里的每个字母按照一张映射表替换成另一个字母比如把a换成q、b换成w。别看它简单想用Python自动破解里面涉及的频率分析、映射推断、迭代逼近这些思路放到现代密码分析里也照样是基本功。这篇文章我会从原理讲到代码再放上一段完整的破解过程和踩坑记录适合Python入门后想接触点实战项目、或者对密码学感兴趣的朋友。1. 项目背景与整体思路拆解1.1 替换密码为什么值得拿来练手单表替换密码在密码学历史上有特殊的地位。古罗马的凯撒密码是它的一个特例所有字母都按固定位数平移而完整的单表替换密码允许任意的一对一映射密钥空间是26的阶乘也就是大概4乘以10的26次方暴力穷举根本不现实。但它的致命弱点在于没有破坏明文的统计特征——英文里e出现最多替换成密文后那个对应的密文字母同样会出现最多。这种统计规律上的泄漏让频率分析成为破解它的杀手锏。用Python来做这件事编程上不需要多高深的技术字典、计数器、字符串处理就能搞定真正的难度在怎么把“统计结果”转化成“可读的明文”。这个转化过程很像做拼图先找到最可能的对应关系然后不断根据上下文修正。我觉得这是非常好的Python实战项目因为它逼着你去思考“数据长什么样”“怎么从数据里提取线索”而不是单纯对着教程敲代码。1.2 破解方案选型人机结合比全自动更靠谱网上有不少号称全自动破解替换密码的脚本说实话对短文本或特殊格式的文本全自动的成功率并不高。原因很简单纯粹靠统计频率推断映射遇到低频字母几乎就是在猜猜错一个可能牵一发而动全身。我实际用的方案是“统计排序 自动初映射 人工交互修正”。先用Python统计密文字母频率和英文标准频率比对生成一个初始猜测表然后程序把密文按照当前映射替换成猜测明文我根据词形、上下文微调映射程序实时刷新结果。整个过程的核心代码并不复杂但很实用也更容易理解破解的每一步在干什么。提示如果你只是想快速跑通流程全自动版本可以看后面的代码如果你希望真正理解破解过程强烈建议用人机交互版本一步步试。1.3 需要准备哪些前置知识做这个项目不需要太多前置知识但有几样东西最好先会Python基础语法字典、列表、字符串的常用方法这些是主力工具对英文单词结构的敏感度看到t e能联想到the看到a能联想到a或i这种直觉在修正映射时特别重要一点点统计思维理解“频率”和“分布”的概念就行不需要懂复杂的统计学公式如果你是完全的Python新手也建议先写几个小练习熟悉一下字典操作比如统计一个字符串里每个字符出现的次数。这个项目里的核心代码本质上就是这类操作的放大版。2. 数据准备与核心原理2.1 英文文本的统计规律破解的理论基础要破解替换密码先得知道英文文本长什么样。我平时用的英文语料统计结果大概是这样的字母e出现频率约为12.7%t约为9.1%a约为8.2%o约为7.5%i约为7.0%n约为6.7%s约为6.3%h约为6.1%r约为6.0%d约为4.3%l约为4.0%剩下的字母频率逐步递减z通常只有0.07%左右。除了单字母频率双字母组合bigram和三字母组合trigram的信息量更大。th是最常见的双字母组合he、in、er紧随其后三字母组合里the是一骑绝尘的王者and、ing也很常见。这些规律在破解时用途很大如果一段密文里某个字母反复出现在词首且和另一个字母经常连在一起那它大概率就是t或s这类常见词首字母。低频字母的信息其实也能用。比如q后面在英文里几乎总是跟着ux和z很少出现在常见英文文本中。如果在密文里发现某个字母几乎只出现在某个特定位置往往能反推出它对应对明文的习惯分布。2.2 频率分析法在替换密码里的应用逻辑替换密码的破解逻辑可以总结成一句话统计规律不随替换而改变。一篇文章里e出现了100次不管换成哪个密文字母那个密文字母依然会出现100次。所以频率分析法分三步走第一步统计密文里每个字母的出现次数按频率从高到低排序。 第二步拿这个排序结果和英文标准频率排序做对比猜测频率最高的密文字母可能对应明文e第二高对应t以此类推。 第三步根据得到的映射关系把密文替换成明文观察结果再用人脑的上下文理解能力修正不合理的映射。这里有个关键点频率排名和高频字母的对应不是死板的。短文本的统计波动很大一篇文章里x可能因为某个单词反复出现而频率暴涨。所以不能机械地按排名一一对应要把排名当成初始提示而不是最终答案。2.3 长短文本的破解难度差异破解难度和密文长度直接相关。我的经验是密文在500个字符以上单表替换密码基本能稳定破解200到500个字符需要多花点心思处理低频字母少于100个字符就算用人脑辅助也很难保证准确率因为统计样本不够频率分布严重失真。如果密文很短比如只有几十个字符可以考虑结合单词模式匹配。比如一个三字母的词首尾字母相同中间字母不同那它很可能对应that、all、see这类模式而不是the。这种模式匹配的思路我会在第4节详细写。3. 核心实现Python代码一步步写出来3.1 环境准备与依赖安装这个项目只需要Python标准库不需要安装任何第三方包所以环境准备非常简单。我用的是Python 3.10理论上Python 3.8以上都能跑。检查一下自己的Python版本即可python --version如果没有Python环境去官网下载安装包安装时记得勾选“Add Python to PATH”选项这能省去后面很多配置环境变量的麻烦。安装完成后在终端输入python能进入交互式界面就算成功。代码组织上我建议建一个项目文件夹里面放一个主脚本文件比如cipher_solver.py。如果后面要加功能可以拆成frequency.py、mapping.py、main.py这种模块结构。这个项目规模不大单文件就够用但养成模块化习惯对以后写复杂项目有帮助。3.2 密文统计模块先看看数据长什么样破解的第一步是摸清密文的底细。我写了一个函数来统计密文里的字母频率同时保留密文中出现的单词结构信息import re from collections import Counter def analyze_ciphertext(cipher_text): 分析密文返回字母频率统计和单词结构信息。 # 只保留字母统一转为小写 letters_only re.sub(r[^a-zA-Z], , cipher_text).lower() # 统计字母频率 freq Counter(letters_only) # 按频率从高到低排序 freq_sorted freq.most_common() # 提取单词列表 words re.findall(r[a-zA-Z], cipher_text) total len(letters_only) freq_with_pct {ch: (count, count / total * 100) for ch, count in freq_sorted} print( 密文字母频率统计 ) for ch, (count, pct) in freq_with_pct.items(): print(f{ch}: {count}次, 占比{pct:.2f}%) print(f\n密文总字母数: {total}) print(f单词数: {len(words)}) print(f前10个高频字母: {[ch for ch, _ in freq_sorted[:10]]}) return freq_with_pct, words这段代码做了三件事过滤出字母、用Counter统计频率、提取单词列表。最后打印出频率表和高频字母列表让我对密文有一个直观感受。运行后你会发现高频字母一目了然接下来就是和英文标准频率对照了。3.3 频率对比与初始映射生成拿到密文字母频率后接下来生成初始映射。我准备了一份英文标准频率表按频率从高到低排列然后把密文的频率排序和它对齐生成一个初步的映射字典# 英文标准频率表按频率从高到低 ENGLISH_FREQ_ORDER [ e, t, a, o, i, n, s, h, r, d, l, c, u, m, w, f, g, y, p, b, v, k, j, x, q, z ] def generate_initial_mapping(freq_sorted): 根据密文频率排序和英文标准频率排序生成初始映射。 返回一个字典密文字母 - 猜测的明文字母 mapping {} for i, (cipher_char, _) in enumerate(freq_sorted): if i len(ENGLISH_FREQ_ORDER): mapping[cipher_char] ENGLISH_FREQ_ORDER[i] return mapping这里有个细节freq_sorted是(字母, 次数)的列表顺序已经按频率排好了。遍历它把第0个密文字母映射到e第1个映射到t以此类推。这样得到的是用标准频率猜测的初始表——它肯定不完全正确但已经能提供相当多的有效信息。英文标准频率表我用了最常见的顺序但不同语料统计出的顺序可能略有差异比如有的语料里s排在h前面。这个差异影响不大因为我们后面还要人工修正初始表只是给一个起点。3.4 替换函数和实时预览有了映射表之后需要把密文按照映射表替换成“猜测明文”。这个函数很简单但要注意处理大小写和未映射的字母def apply_mapping(cipher_text, mapping): 把密文按映射表替换成猜测明文。 cipher_text: 原始密文保留大小写和标点 mapping: 字典 {密文字母: 明文字母} result [] for ch in cipher_text: lower_ch ch.lower() if lower_ch in mapping: mapped mapping[lower_ch] if ch.isupper(): result.append(mapped.upper()) else: result.append(mapped) else: result.append(ch) # 非字母或未映射字母保持不变 return .join(result)这里的迭代逻辑是对密文里的每个字符先转成小写去查映射表找到后根据原来的大小写情况还原成对应的大小写形式。没在映射表里的字符保持原样——这么做是为了保留空格和标点方便观察单词边界。3.5 人机交互修正循环破解的核心体验自动替换只是破了一半另一半在于根据可读性修正映射表。我写了一个交互式循环可以实时查看当前解密效果手动调整某个字母的映射def interactive_crack(cipher_text, mapping): 交互式破解循环 1. 显示当前解密效果 2. 用户输入要修改的密文字母和对应的明文字母 3. 实时更新映射并刷新显示 print(\n 当前解密效果 ) print(apply_mapping(cipher_text, mapping)) while True: print(\n输入格式: 密文字母 明文字母 (例如: q e)) print(输入 quit 结束, 输入 reset 重置映射) user_input input( ).strip() if user_input.lower() quit: break if user_input.lower() reset: return reset parts user_input.split() if len(parts) ! 2: print(输入格式错误请按照 密文字母 明文字母 格式输入) continue cipher_char parts[0].lower() plain_char parts[1].lower() # 检查输入合法性 if len(cipher_char) ! 1 or len(plain_char) ! 1 or not cipher_char.isalpha() or not plain_char.isalpha(): print(请输入单个字母) continue mapping[cipher_char] plain_char print(\n 更新后的解密效果 ) print(apply_mapping(cipher_text, mapping))这个循环的关键设计在于允许用户随时输入新的映射关系覆盖之前的猜测。比如我发现q这个密文字母在文本里频繁出现在词尾它的频率排名又很高那我可能把q从映射到e改成映射到s。程序会立刻刷新整个文本所有q都会被替换成s这个反馈极其直观。3.6 辅助函数检查映射冲突在实际交互中很容易出现一个问题把两个不同的密文字母映射到了同一个明文字母这就破坏了替换密码的双射性质。我写了个检查函数来避免这个坑def check_mapping_conflicts(mapping): 检查映射是否存在冲突两个不同的密文字母是否映射到了同一个明文字母。 used_plain {} conflicts [] for cipher_char, plain_char in mapping.items(): if plain_char in used_plain: conflicts.append((used_plain[plain_char], cipher_char, plain_char)) else: used_plain[plain_char] cipher_char return conflicts如果检测到冲突程序会给出提示告诉用户哪两个密文字母同时想映射到同一个明文字母。这在手动解锁新字母时非常有用——有时候你只是想把某个密文字母改成e没注意到密文里另一个字母已经占用e了结果整段文本乱套。有了这个检查至少能帮忙意识到问题出在哪。4. 实操过程与完整破解实验4.1 构造一个测试用例从加密到破解自己调试的时候最好有一个已知明文的测试用例这样能验证每一步代码是否正确。我先写了一个加密函数用随机生成的映射表加密一段英文文本然后拿密文去破解import random import string def generate_random_mapping(): 生成一个随机的替换映射表 letters list(string.ascii_lowercase) shuffled letters.copy() random.shuffle(shuffled) mapping {} for i in range(26): mapping[letters[i]] shuffled[i] return mapping def encrypt(text, mapping): 用映射表加密明文 result [] for ch in text: lower_ch ch.lower() if lower_ch in mapping: mapped mapping[lower_ch] if ch.isupper(): result.append(mapped.upper()) else: result.append(mapped) else: result.append(ch) return .join(result)测试用的明文我选取了一段英文日常文本既包含常见词汇又有一定的长度这样频率特征比较明显。加密后的密文会呈现完全不可读的状态接下来就是见证破解过程的时候。4.2 完整解密过程实录我用上面那段代码加密了一段约600字符的英文文本生成密文后开始破解。以下是真实过程中的关键节点初始频率分析结果密文里最高频的字母是v占比11.2%第二是j占比9.8%第三是f7.9%。对照英文标准频率我先把v映射到ej映射到tf映射到a。初始替换后文本大概是这样的jfe ...看起来像是t?e或者the——等一下jfe如果对应的是t?e那意味着f应该对应h而不是a。这就引出一个重要经验初始映射通常需要立即调整尤其是前三个字母的对应不能全信。我把j改成映射到tf改成映射到hv保持映射到e于是jfe完美变成the。一鼓作气我观察到很多单词以q开头把q映射成w后原本一些不可读的词开始出现wh或w?的模式。继续观察bcd这个字母组合反复出现大概率对应and于是把b映射为ac映射为nd映射为d。当前进度已经识别出一批高频词the、and、ing、ion映射表完成了大约一半低频字母如z、x、q映射关系还没有确定接下来处理低频字母。我搜索密文里的残留单词发现一个模式是w?o结合上下文判断可能是who或two。通过上下文确认w映射为w那个未知的?对应h或o。这种利用上下文猜测的方法效率比单纯的频率对照高得多。4.3 模式匹配解决低频字母的武器当拥有较短密文时模式匹配就成了破解的重要补充。我的代码里加了一个函数用来在密文中查找与给定单词模式匹配的单词def find_pattern_words(words, pattern): 在密文单词列表中查找与给定模式匹配的单词。 pattern 示例: abc 表示三个不同字母的模式 result [] for word in words: word word.lower() if len(word) ! len(pattern): continue mapping_local {} used set() match True for i, ch in enumerate(word): if pattern[i] not in mapping_local: if ch in used: match False break mapping_local[pattern[i]] ch used.add(ch) else: if mapping_local[pattern[i]] ! ch: match False break if match: result.append(word) return result比如我想找出所有和that模式一致的密文单词就用find_pattern_words(words, abcd)——这个词要求第一位和第四位字母相同第二位是另外的字母第三位又是另一个字母。运行后可能会找到类似sxxs这样的模式结合上下文就能推断出它对应的是that还是all。4.4 从半成品到完整明文实战中的迭代修正继续前面的破解过程。在确定了大约15个映射之后解出的文本已经能读懂一部分了。比如其中一句话已经显示为we c?n ?e? ?he ...这时人脑的优势就体现出来了c?n大概率是can所以c已经映射对了那个?应该是a?e?可能是get、let或yet结合上下文判断出是get。这种迭代过程持续了大约10分钟最后所有字母的映射都确定了。完整的明文被还原出来和原始明文对比只有个别词因为我一开始映射错了又没检查出来而出现差异后来修正后就完全一致了。这里分享一个关键经验修正映射时优先改频率最高的字母因为它们的出错影响范围最大。一个e被映射错整篇文本里所有本该是e的地方全部乱套反过来一个x或z映射错了可能只影响一两个词。5. 常见问题与排查技巧5.1 密文太短导致频率失真怎么办这是我在实际项目里遇到最多的一个问题。当密文只有几十或上百个字符时某个字母可能因为一个单词反复出现而频率虚高比如一个100字符的密文里m出现12次可能只是因为明文中含有一个反复出现的关键词。应对策略有三个。第一不要盲从频率排名而是结合词形模式把密文里的单词按长度和重复模式分类找规律。第二多利用高频词的固定模式比如三字母的the、and双字母的of、to、in、it、is、as、at、be、by等。第三如果确实无法破解就标记为“样本不足”采用人机交互方式人工推断——这不是失败而是换了一种更合理的思路。5.2 标点符号和空格该怎么处理我在设计代码时选择保留标点和空格因为它们给破解提供了宝贵的单词边界信息。空格分隔出的单词形状能直接辅助判断比如看到一个一字母单词它只能是a或i这个信息非常有用。遇到标点符号的处理原则很简单标点不参与映射原样保留。代码里已经是这么做的了——先按字母判断找不到映射就直接把字符原样添加到结果里。这样既简化了实现也不会丢失信息。区分大小写的问题我的建议是破解过程统一用小写字母处理最后如果需要输出规范文本再根据句子结构还原大小写。因为大小写通常不影响密码分析的逻辑反而会给统计增加复杂度。实际输出时我遇到了几个全大写单词处理方式是在apply_mapping里输出时把明文字母转换成大写。5.3 映射冲突和误判怎么排查手动修正映射表时经常会出现冲突。比如我先设置了x - t后来根据某个词又想把y - t这时候t就被占了。代码里的check_mapping_conflicts函数能发现这个情况但真正重要的是如何决定哪一方是对的。我的排查顺序是先看冲突的两个密文字母在哪些词里出现然后看哪个映射能让更多词变得可读。如果y - t能让三个本来看不懂的词变成the、that、they而x - t只让一个词看起来合理那大概率是x的设置不对应该把x改回未映射状态甚至去重新推断它的取值。5.4 密文里夹杂数字和其他符号的情况替换密码理论上只替换字母但实际场景中密文可能包含数字。如果数字是明文的一部分而不是被替换的密文那它们应该被保留。还有一种可能加密者把数字也纳入了替换范围比如1 - 7。这种情况比较罕见传统替换密码不涉及数字如果遇到多半是自定义加密方案。我的处理方式是先按纯字母假设破解如果发现某些数字反复出现在特定位置、影响了解读再考虑把它们纳入映射表。扩展方式也很简单——在映射表里加数字键比如mapping[1] e前提是统计确认数字确实承载了字母信息。5.5 自动破解脚本为什么有时会失败我写过一个尝试全自动破解的版本思路是频率排序生成映射然后根据字典词匹配反馈修正映射循环直到不再变化。这个版本在长文本上表现不错但遇到短文本就有点绝望一两个错误的映射就能让后续所有步骤错下去而且没有人工干预的话错误会不断累积。后来我复盘原因全自动脚本缺少“上下文语义判断”它只能依赖统计规律和词典匹配但词典匹配本身就有问题——一个词匹配到多个候选词时程序不知道哪个在语境里更合理而人脑能轻松完成这个判断。所以我的建议是自动脚本用来做粗筛和初始映射最终结果靠人机交互修正这才是稳定可靠的方案。注意这里说的“破解”对象应该是自己构造的密文或授权的学习材料。在真实场景中擅自解密他人的密文涉及法律和道德问题请务必在法律允许的范围内进行学习研究。6. 扩展思路与实际应用6.1 从单表替换到多表替换维吉尼亚密码的挑战单表替换破解成功后很自然地会想到如果加密者采用多表替换怎么办维吉尼亚密码就是最典型的多表替换它用一个关键词循环控制每个字母的替换表同一字母在明文不同位置可能映射到不同的密文字母。这种设计直接破坏了单表替换密码的频率分布特征让简单的频率分析失效。Python破解维吉尼亚密码的思路完全不同先通过卡西斯基测试或重合指数估算密钥长度再按密钥长度把密文分组对每一组分别做频率分析恢复到单表替换的场景。这个流程说白了就是把一个复杂问题拆成几个简单问题。如果你想在替换密码项目之后继续进阶我强烈推荐研究维吉尼亚密码它能让你理解为什么单表替换容易破解、而多表替换在历史上保持了更久的安全性。6.2 这个项目还能怎么玩词频分析、Markov链与自动破解除了基本的人机交互破解Python还能做更多。我尝试过用Markov链模型来评估候选明文的合理性构建一个英文文本的字母转移概率矩阵计算一段候选明文作为真实英文的概率。步进搜索的时候每次调整映射表都计算一下新结果的评分以此引导自动搜索方向减少人工干预量。这个方向扩展出来的一个小练习是写一个爬山算法从一个初始映射出发随机交换两个字母的映射如果评分提升就保留不断迭代直到收敛。虽然爬山算法容易陷入局部最优解但作为学习启发式搜索的入门例子非常经典。我自己的实践结果显示对长文本简单的爬山算法配Markov链评分就能在大多数情况下自动解出明文——全自动破解并不是不能做只是需要更聪明的评分函数。6.3 密码学学习中Python的角色从替换密码开始学古典密码再慢慢过渡到对称加密、非对称加密这个路线我比较推荐。替换密码虽然古老但它涉及的核心概念——密钥空间、映射、统计规律、破译——在现代密码学里仍然是重要的思维框架。当你理解了为什么替换密码不安全才能真正理解为什么现代加密算法要设计混淆和扩散为什么一次一密在理论上是无法破解的。Python在这条学习路线里的作用是巨大的。它能帮你快速实现加密和解密逻辑验证理论推导能帮你做统计分析从数据中找出规律还能帮你构建交互式工具在学习过程中获得实时反馈。替代密码是这个路线的第一站门槛低、反馈强、成就感足非常适合作为第一个密码学编程项目。6.4 给新手的几个实战建议最后分享几个从实际调试中总结的经验第一不要一上来就追求全自动破解。先用交互式版本手动跑几轮体会“从统计规律到语义理解”的转换过程这对调试能力的提升比单纯跑代码有用得多。第二保留一份标准频率表和一份常见英文高频词表。这两个数据是破解的“弹药库”很多困惑都能靠它们解决。高频词表建议包含the、and、that、have、for、not、with、you、this、but等常用词以及of、to、in、it、is、as、at、be、by等双字母词。第三写代码时多打印中间结果。我调试时习惯在每次映射更新后打印当前解密效果这一步能直观地看到进展也方便定位哪里出了问题。不要等全部跑完才看结果那样很难找到错误源头。第四如果某一步卡住了暂停一下换一个角度去看。不要一直纠结于某个低频字母的映射先把已经确定的高频字母理顺往往回过头来那个问题自然就解决了。我很多次破解卡壳最后都是通过“先解决更确定的部分”来推进的。替换密码的破解核心并不是“写代码”而是“观察、假设、验证、修正”这套分析循环。Python是这套循环的放大器它让统计和替换变得极快把时间留给你去思考。哪怕是几行简单的统计代码配合一个交互式的替换函数也能还原出一整段原本不可读的文字。这种从混乱到有序的过程正是编程和密码学结合的乐趣所在。如果你手头有一段自己加密的文本不妨按这个思路跑一遍你会在实践中发现更多文档里没有写的细节。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价