
1. 项目概述为什么今天还要看古典加密提起加密很多人脑子里蹦出来的可能是AES、RSA这些现代密码学明星或者区块链、数字货币这些时髦概念。作为一名在信息安全领域摸爬滚打了十多年的老兵我常常被问到“那些几百上千年前的古典加密算法比如凯撒密码、维吉尼亚密码现在还有啥用不是早就被破解得干干净净了吗”这个问题问到了点子上。我们今天深入探索古典加密算法尤其是其中的两大基石——置换密码与代换密码其目的绝非是为了用它们来保护今天的敏感数据。那无异于用木盾去挡子弹。真正的价值在于它们是理解现代密码学大厦的“地基”和“脚手架”。每一个复杂的现代加密系统其核心思想都能在古典算法中找到雏形。学习置换与代换就像学编程先学“Hello World”学数学先学加减乘除。它们用最直观的方式揭示了信息隐藏最本质的两种思路打乱顺序置换和替换内容代换。对于安全从业者这是构建知识体系不可或缺的一环对于编程爱好者这是理解算法思维的绝佳案例甚至对于普通读者这也能让你在侦探小说或历史剧里看懂那些“密码信”到底是怎么一回事。接下来我们就抛开晦涩的教科书定义用实操和案例把这看似古老的智慧拆解明白。2. 核心原理拆解两种最基础的加密思想所有加密算法无论古典还是现代其目标都是将可读的明文通过某种规则转换为不可读的密文。古典加密算法主要围绕这个转换规则做文章而置换和代换就是设计规则的两条最根本的路径。2.1 代换密码字母的“变身术”代换密码的核心思想非常简单直接将明文中的每一个字符按照某种固定的规则替换成另一个字符。你可以把它想象成一份“密码对照表”。最著名、最简单的例子就是凯撒密码。它的规则是将字母表中的每个字母向后或向前移动一个固定的位数。例如当偏移量为3时A变成DB变成E……X变成AY变成BZ变成C。那么“HELLO”加密后就变成了“KHOOR”。注意凯撒密码是一种单表代换密码即在整个加密过程中只使用一张固定的替换表。这是它最大的弱点因为一旦替换规则被猜出或通过频率分析破解整个密文就完全暴露了。为了增强安全性古人又发明了多表代换密码代表作是维吉尼亚密码。它不再使用一张固定的表而是使用一个关键词来生成多张替换表循环使用。比如关键词是“KEY”明文是“ATTACK”。那么加密时用K对应的表加密A用E对应的表加密T用Y对应的表加密第二个T然后再循环使用K加密A……这样同一个明文字母如T在不同位置可能被替换成不同的密文字母大大增加了频率分析的难度。代换密码的本质是改变了信息的“内容”本身但保留了字符的“顺序”。它像是在玩一个全局的“找不同”游戏。2.2 置换密码顺序的“大洗牌”与代换密码相反置换密码不改变字符本身而是打乱明文字符的排列顺序。它像是一副扑克牌牌还是那些牌但通过洗牌让顺序变得无法辨认。最常见的置换密码是栅栏密码。假设我们使用2栏栅栏加密“HELLOWORLD”。将明文写成两行 H L O O L E L W R D然后按行读取得到密文“HLOOLELWRD”。更复杂一点的比如列置换密码。我们需要一个密钥例如“GERMAN”。加密“ATTACKPOSTPONEDUNTILTWOAM”将密钥字母按字母表顺序编号G(3), E(2), R(5), M(4), A(1), N(6)。将明文按密钥长度6写成矩阵A T T A C K P O S T P O N E D U N T I L T W O A M X X X X X (用X填充空白)按照密钥编号的顺序1,2,3,4,5,6 - A,E,G,M,N,R读取每一列 第1列APNIM - APNIM 第2列TOELX - TOELX 第3列TSD TX - TSDTX ... 最终得到杂乱无章的密文。置换密码的本质是保留了信息的“内容”但彻底破坏了其“顺序”。它像是在玩一个复杂的“拼图”游戏只有知道拼图规则密钥的人才能复原。2.3 混合与演进古典加密的终极形态聪明的古人很快发现单一的代换或置换都不够安全。于是将两者结合起来的乘积密码出现了。最经典的例子是A5/1流密码用于早期GSM通信的思想前身或者更直观的我们可以手工模拟先对明文进行一次列置换打乱顺序再对结果进行一次凯撒代换改变内容。这样即使攻击者破解了其中一层仍然面对另一层的障碍。这种“先置换再代换”或“多轮代换-置换网络”的思想直接启发了现代分组密码如DES、AES的设计核心。AES加密的每一轮都包含了字节代换、行移位、列混合和轮密钥加等步骤本质上就是代换和置换操作的精巧、复杂、多轮的组合。理解了古典的置换与代换再看AES的轮函数就会有豁然开朗的感觉。3. 手工实现与代码模拟从理解到创造光说不练假把式。要真正吃透这些算法最好的办法就是亲手实现它们。这里我用Python来演示因为其语法清晰贴近自然语言非常适合表达算法逻辑。3.1 凯撒密码的Python实现def caesar_cipher(text, shift, modeencrypt): 实现凯撒密码的加密和解密。 :param text: 输入文本仅处理字母 :param shift: 偏移量整数 :param mode: encrypt 或 decrypt :return: 加密或解密后的文本 result [] # 解密时偏移方向相反 if mode decrypt: shift -shift for char in text: if char.isupper(): # 对大写字母操作A的ASCII码是65 new_char chr((ord(char) - 65 shift) % 26 65) result.append(new_char) elif char.islower(): # 对小写字母操作a的ASCII码是97 new_char chr((ord(char) - 97 shift) % 26 97) result.append(new_char) else: # 非字母字符原样保留 result.append(char) return .join(result) # 实操示例 plaintext Hello, Cryptography! shift 3 ciphertext caesar_cipher(plaintext, shift, encrypt) print(f明文: {plaintext}) print(f密文: {ciphertext}) print(f解密: {caesar_cipher(ciphertext, shift, decrypt)})实操心得在实现时最容易出错的地方就是取模运算% 26和ASCII码的基准值65或97。务必记住这个操作是为了让字母表“循环”起来Z之后回到A。忘记取模会导致字符超出字母范围得到奇怪的符号。3.2 列置换密码的Python实现列置换密码的实现稍复杂关键在于密钥排序和按列读取。def columnar_transposition(text, key, modeencrypt): 实现列置换密码的加密和解密。 :param text: 输入文本通常去除空格 :param key: 密钥单词如GERMAN :param mode: encrypt 或 decrypt # 去除文本中的空格并转换为大写以便统一处理 text text.replace( , ).upper() key key.upper() num_cols len(key) num_rows -(-len(text) // num_cols) # 向上取整计算所需行数 # 加密过程 if mode encrypt: # 用X填充矩阵空白 while len(text) num_rows * num_cols: text X # 创建二维矩阵并按行填充 matrix [list(text[i*num_cols:(i1)*num_cols]) for i in range(num_rows)] # 确定密钥字母的读取顺序先给密钥字母编号按字母表顺序 key_order sorted([(char, idx) for idx, char in enumerate(key)]) # 按编号顺序读取每一列形成密文 ciphertext for _, original_idx in key_order: for row in range(num_rows): ciphertext matrix[row][original_idx] return ciphertext # 解密过程逆过程 elif mode decrypt: # 计算填充字符数 num_fill (num_rows * num_cols) - len(text) # 确定密钥字母顺序 key_order sorted([(char, idx) for idx, char in enumerate(key)]) # 创建一个空矩阵 matrix [[ for _ in range(num_cols)] for _ in range(num_rows)] # 按加密时列的顺序将密文字符按列写回矩阵 text_idx 0 for _, original_idx in key_order: for row in range(num_rows): # 如果是最后几列且处于填充行可能需要跳过 if row num_rows - 1 and original_idx num_cols - num_fill: # 这个位置是填充的X在解密时我们忽略它 # 更严谨的做法是记录填充位置这里为简化先跳过赋值 pass else: if text_idx len(text): matrix[row][original_idx] text[text_idx] text_idx 1 # 按行读取矩阵得到明文 plaintext for row in range(num_rows): for col in range(num_cols): if matrix[row][col]: plaintext matrix[row][col] # 移除尾部可能存在的填充字符X根据约定 plaintext plaintext.rstrip(X) return plaintext # 实操示例 plaintext ATTACK POSTPONED UNTIL TWO AM key GERMAN ciphertext columnar_transposition(plaintext, key, encrypt) print(f密钥: {key}) print(f明文: {plaintext}) print(f密文: {ciphertext}) print(f解密: {columnar_transposition(ciphertext, key, decrypt)})踩坑记录列置换的解密算法比加密复杂得多是新手最容易卡住的地方。核心在于加密时“按排序后的列顺序写出”解密时必须“按同样的顺序读回”。我强烈建议在纸上画出一个3x4的小矩阵手动走一遍加密和解密的流程理解字符是如何“按列写入、按列读出”又“按列读回、按行读出”的。理解了这个过程代码逻辑就清晰了。4. 古典密码的破解实战频率分析与唯密文攻击既然实现了加密作为一个安全研究者我们更感兴趣的是如何破解。古典密码的破解艺术在计算机诞生前就已登峰造极其核心方法是频率分析。这对于理解现代密码分析学的基础至关重要。4.1 单表代换密码的致命弱点凯撒密码等所有单表代换密码都有一个无法掩盖的缺陷它们保留了原始语言的字母频率统计特征。在英文中字母‘E’的出现频率远高于‘Z’。无论你把‘E’替换成什么字符比如替换成‘X’那么在密文中‘X’的出现频率就会异常地高。破解步骤通常如下统计频率统计密文中所有字符的出现频率。匹配猜测将频率最高的密文字母猜测为‘E’次高的猜测为‘T’或‘A’根据英文常见频率ETAOIN...。模式分析寻找常见的单词模式如单字母单词很可能是‘A’或‘I’固定后缀如“-ing”“-tion”高频双字母“th”“he”“in”等。试探与调整根据猜测替换部分密文结合上下文语义逐步修正猜测最终还原出整个替换表。我们可以写一个简单的Python程序来辅助频率分析def frequency_analysis(ciphertext): 对密文进行简单的字母频率分析。 ciphertext .join(filter(str.isalpha, ciphertext.upper())) total_letters len(ciphertext) freq {} for char in ciphertext: freq[char] freq.get(char, 0) 1 # 按频率降序排序 sorted_freq sorted(freq.items(), keylambda x: x[1], reverseTrue) print(字母频率统计降序:) for char, count in sorted_freq: percentage (count / total_letters) * 100 print(f {char}: {count}次 ({percentage:.2f}%)) return sorted_freq # 示例分析一段用单表代换加密的密文 cipher_sample XLMW MW E WXITXIV QIWWEKI MR XLI GSQTPI frequency_analysis(cipher_sample)运行这段代码你会看到密文中‘X’和‘I’的频率非常高。结合英文频率表可以大胆猜测‘X’对应‘E’‘I’对应‘T’。再结合“MW”这个高频双字母组合很可能对应“IS”或“AN”就能很快破译出这段密文实际上是“THIS IS A SECRET MESSAGE IN THE CIPHER”使用了偏移量为4的凯撒密码。4.2 对抗频率分析多表代换与密码的演进频率分析如此有效迫使加密者寻找新方法。维吉尼亚密码等多表代换的出现就是为了“抹平”频率特征。使用关键词“KEY”加密一段长文本明文中所有的‘E’会被循环地替换成‘I’K对应、‘C’E对应、‘G’Y对应等从而使得密文中单个字母的频率分布趋于平均更接近随机分布让简单的频率分析失效。重要概念这引出了密码学中一个核心概念——混淆与扩散。混淆指密文与密钥之间的关系应尽可能复杂让攻击者无法从密文中推知密钥。代换操作是实现混淆的主要手段。例如复杂的S盒扩散指明文中的一位发生变化应导致密文中多位发生无法预测的改变同时密文中每一位都应依赖于明文中多位。置换操作是实现扩散的主要手段。例如P盒、行移位 现代密码如AES的设计目标就是通过多轮精巧的混淆和扩散操作使得密文统计特性与随机序列无法区分。破解维吉尼亚密码需要更高级的技术如卡西斯基试验和重合指数法其核心是先猜测密钥长度然后将密文按密钥长度分割成多个“小组”每个小组实际上是一个单表代换密码因为用的是同一个密钥字母再分别对每个小组进行频率分析。这个过程已经显露出分而治之的现代密码分析思想。5. 从古典到现代思想传承与安全启示通过上面的探索我们可以看到古典加密算法绝非过时的玩具。它们清晰地勾勒出了密码学发展的脉络和对抗的主线。5.1 古典密码的现代“转世”代换 - S盒古典的代换表进化成了现代分组密码中核心的S盒。AES的S盒是一个16x16的查找表完成非线性字节代换提供强大的混淆功能。设计一个抗差分和线性分析的S盒是现代密码学的重要课题。置换 - P盒/行移位/列混合古典的栅栏、列置换进化成了P盒位置换、行移位和列混合操作。在AES中行移位和列混合操作确保了比特位在状态矩阵中快速扩散。维吉尼亚密码 - 流密码维吉尼亚密码那种使用密钥流与明文结合模加的模式正是流密码的思想雏形。现代流密码如ChaCha20使用更复杂的伪随机数生成器产生密钥流安全性更高。乘积密码 - 迭代轮结构将代换和置换多次组合形成了现代分组密码的多轮迭代结构如Feistel网络、SPN结构。每一轮都包含混淆和扩散步骤经过足够多的轮数后达到足够的安全强度。5.2 对现代应用的安全启示学习古典密码的破解能给我们今天的设计和使用带来宝贵的“反面教材”不要自己发明加密算法古典密码的失败史告诉我们设计一个能抵抗各种未知攻击的密码系统极其困难。今天绝对不要试图自己创造加密算法用于实际系统。务必使用经过全球密码学家多年公开检验、标准化的算法如AES-GCM、ChaCha20-Poly1305。密钥管理比算法本身更重要许多古典密码被破不是因为算法被攻破而是因为密钥泄露或太短如维吉尼亚密码的短密钥。这启示我们在现代系统中安全的密钥生成、存储、分发和轮换其重要性不亚于选择强算法。“安全通过 obscurity”是无效的古典时期人们常常试图通过隐藏算法本身“隐晦式安全”来获得安全。历史证明这完全无效。现代密码学的基石是Kerckhoffs原则系统的安全性应仅依赖于密钥的保密而不依赖于算法的保密。算法必须公开经得起最挑剔的审视。理解威胁模型凯撒密码在2000年前可能够用因为它当时的对手不具备频率分析的知识和能力。今天你的系统面临的对手是谁是脚本小子是有组织的犯罪集团还是国家级的攻击力量理解威胁模型才能选择合适的安全措施。古典密码的学习训练了我们这种“攻击者视角”的思维。6. 延伸实验与趣味应用理解了原理我们可以做一些有趣的延伸实验让知识更加牢固。6.1 组合密码实验尝试将置换和代换组合起来自己设计一个简单的“乘积密码”。例如先对明文进行一次列置换密钥1。再对结果进行一次凯撒密码加密密钥2偏移量。甚至可以多次迭代。编写加解密函数并尝试用频率分析攻击它。你会发现即使每层都很弱但组合起来后破解难度显著增加。这直观地验证了“混淆与扩散”结合的力量。6.2 现实中的古典密码“遗产”你以为古典密码完全消失了吗并非如此它们常以趣味或弱安全的形式出现ROT13偏移量为13的凯撒密码常用于论坛隐藏剧透、答案。因为26个字母加密两次即解密无需单独写解密函数。报纸上的谜题很多字谜游戏本质上就是简单的单表代换。历史重现在一些历史主题的密室逃脱或游戏中会故意使用古典密码作为线索增加趣味性。教学与启蒙毫无疑问这是它们最重要的现代价值也是我们这篇文章的核心所在。最后我个人的体会是密码学就像一门“防御的艺术”而学习古典密码就是参观这门艺术的历史博物馆。你看到的每一件展品算法都铭刻着当时攻防双方最顶尖的智慧。它们简陋但原理纯粹它们易破但思想永恒。当你下次再看到AES-256-GCM这样的术语时希望你能会心一笑因为你知道在那层层复杂的数学包装之下跳动着的依然是“置换”与“代换”这两个古老而坚韧的心脏。