第三章:古典密码学
3.1 凯撒密码(Caesar Cipher)
原理: 凯撒密码是一种最简单的替换密码,将字母表中的每字母固定偏移K个位置来实现加密。
加密公式: C = (P + K) mod 26
解密公式: P = (C - K) mod 26
其中P为明文字母的编号(A=0, B=1, ..., Z=25),C为密文字母的编号,K为偏移量。
解密方法: 由于密钥空间仅有26种可能(偏移量0-25),可以暴力枚举所有偏移量。
# 凯撒密码暴力破解脚本
def caesar_brute_force(ciphertext): "
""暴力枚举所有26种偏移量,打印所有可能的明文"""
for shift in range(26):
plaintext = ""
for char in ciphertext:
if char.isalpha():
# 确定基准字符(大写或小写)
base = ord('A') if char.isupper() else ord('a')
# 解密:向后偏移shift个位置
decrypted = chr((ord(char) - base - shift) % 26 + base)
plaintext += decrypted
else:
plaintext += char
print(f"偏移量 {shift:2d}: {plaintext}")
# 示例
ciphertext = "Fydw{Fdhvdu_Flskhu_Lv_Hdvb}"
print("=== 凯撒密码暴力破解 ===") caesar_brute_force(ciphertext)
# 偏移量 3 时得到:
Flag{Caesar_Cipher_Is_Easy}
|-------------------------------------------------------------------------------------------------------------------------|
| 💡 CTF 技巧 使用CyberChef的 ROT13 或 ROT13 Brute Force 功能可以快速尝试所有偏移。ROT13是凯撒密码偏移量为13的特例,由于26/2=13,ROT13加密和解密操作相同。 |
3.2 仿射密码(Affine Cipher)
原理: 仿射密码是凯撒密码的推广,使用线性函数进行加密。
加密: C = (a × P + b) mod 26
解密: P = a⁻¹ × (C - b) mod 26
约束条件: GCD(a, 26) = 1,即a与26互素,保证a有模逆元。
合法的 a 值: 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25(共12个)。
密钥空间:12 × 26 = 312种组合,可暴力枚举。
def affine_decrypt(ciphertext, a, b):
"""仿射密码解密"""
# 计算a的模逆元
a_inv = pow(a, -1, 26)
plaintext = ""
for char in ciphertext:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
c = ord(char) - base
p = (a_inv * (c - b)) % 26
plaintext += chr(p + base)
else:
plaintext += char
return plaintext def affine_brute_force(ciphertext):
"""仿射密码暴力破解"""
from math import gcd
for a in range(1, 26):
if gcd(a, 26) != 1:
continue
for b in range(26):
result = affine_decrypt(ciphertext, a, b)
# 检查结果中是否包含常见flag格式
if 'flag' in result.lower() or 'ctf' in result.lower():
print(f"a={a}, b={b}: {result}")
# 示例
ciphertext = "Nxkq{Knnbgz_Vbiazm}" affine_brute_force(ciphertext)
# 找到合适的a和b后输出解密结果
3.3 维吉尼亚密码(Vigenère Cipher)
原理: 维吉尼亚密码是一种多表替换密码。使用一个关键词(keyword)作为密钥,关键词的每个字母决定对明文对应位置的偏移量。关键词循环使用。
加密: C_i = (P_i + K_{i mod len(K)}) mod 26
解密: P_i = (C_i - K_{i mod len(K)}) mod 26
破解方法一: Kasiski 检验法
- 在密文中寻找重复的字符片段(通常3个字母以上)
- 计算重复片段之间的间距
- 密钥长度很可能是这些间距的公约数
- 确定密钥长度后,对每组字母分别进行频率分析
破解方法二:频率分析
-
确定密钥长度L后,将密文按位置分为L组
-
每组相当于一个独立的凯撒密码
-
对每组进行频率分析,找出最高频字母,假设其对应英文中的'E'
-
由此推算每组的偏移量,组合得到完整密钥
from collections import Counter from math import gcd from functools import reduce def kasiski_examination(ciphertext, min_len=3):
"""Kasiski检验:寻找重复片段,推测密钥长度"""
text = ''.join(c.upper() for c in ciphertext if c.isalpha())
distances = []
寻找长度>=min_len的重复片段
for length in range(min_len, len(text) // 2):
for i in range(len(text) - length):
pattern = text[i:i+length]
j = text.find(pattern, i + 1)
while j != -1:
distances.append(j - i)
j = text.find(pattern, j + 1)
if not distances:
return []
计算所有间距的因数
factor_count = Counter()
for d in distances:
for f in range(2, d + 1):
if d % f == 0:
factor_count[f] += 1
返回最可能的密钥长度
return factor_count.most_common(5) def vigenere_decrypt(ciphertext, key):
"""维吉尼亚密码解密"""
key = key.upper()
plaintext = ""
key_idx = 0
for char in ciphertext:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
shift = ord(key[key_idx % len(key)]) - ord('A')
decrypted = chr((ord(char) - base - shift) % 26 + base)
plaintext += decrypted
key_idx += 1
else:
plaintext += char
return plaintext def frequency_analysis_crack(ciphertext, key_length):
"""通过频率分析破解维吉尼亚密码"""
text = ''.join(c.upper() for c in ciphertext if c.isalpha())
key = ""
for i in range(key_length):
提取第i组字母
group = text[i::key_length]
统计频率
freq = Counter(group)
假设最高频字母对应'E'(编号4)
most_common = freq.most_common(1)[0][0]
shift = (ord(most_common) - ord('E')) % 26
key += chr(shift + ord('A'))
return key
示例用法
ciphertext = "Lxfopv ef rnhr" key = "KEY" print(vigenere_decrypt(ciphertext, key))
3.4 栅栏密码(Rail Fence Cipher)
原理: 将明文按照锯齿形(zigzag)排列到指定行数的"栅栏"上,然后按行读取得到密文。
示例: 明文 "WEAREDISCOVERED",栅栏数为3:
W . . . E . . . S . . . E . . . E . R . D . S . O . R . D . . . A . . . I . . . V . . . . 密文:WESE ERDSORD AIVO (按行读取拼接)
def rail_fence_decrypt(ciphertext, num_rails):
"""栅栏密码解密"""
if num_rails <= data-id="367" 1:
return ciphertext
n = len(ciphertext)
# 构建栅栏模式,确定每个位置属于哪一行
pattern = []
for i in range(n):
cycle = 2 * (num_rails - 1)
pos = i % cycle
row = pos if pos < num_rails else cycle - pos
pattern.append(row)
# 计算每行的字符数
row_lengths = [0] * num_rails
for r in pattern:
row_lengths[r] += 1
# 将密文分配到各行
rows = []
idx = 0
for length in row_lengths:
rows.append(list(ciphertext[idx:idx+length]))
idx += length
# 按模式重建明文
row_indices = [0] * num_rails
plaintext = ""
for r in pattern:
plaintext += rows[r][row_indices[r]]
row_indices[r] += 1
return plaintext
# 暴力尝试不同栅栏数 def rail_fence_brute_force(ciphertext, max_rails=20): """暴力尝试所有可能的栅栏数"""
for rails in range(2, min(max_rails, len(ciphertext))):
result = rail_fence_decrypt(ciphertext, rails)
print(f"栅栏数 {rails}: {result}")
# 示例
cipher = "WECRLTEERDSOEEFEAOC" rail_fence_brute_force(cipher, 10)
3.5 培根密码(Bacon's Cipher)
原理: 由弗朗西斯·培根发明,使用5位二进制编码(A和B的组合)来表示字母。
| 字母 | 编码 | 字母 | 编码 | 字母 | 编码 |
|---|---|---|---|---|---|
| A/a | AAAAA | J/i | ABAAA | S | BAAAB |
| B | AAAAB | K | ABAAB | T | BAABA |
| C | AAABA | L | ABABA | U/V | BAABB |
| D | AAABB | M | ABABB | W | BABAA |
| E | AABAA | N | ABBAA | X | BABAB |
| F | AABAB | O | ABBAB | Y | BABBA |
| G | AABBA | P | ABBBA | Z | BABBB |
| H | AABBB | Q | ABBBB | ||
| I/J | ABAAA | R | BAAAA |
隐写形式: 在CTF中常见用大小写字母(小写=A,大写=B)或两种不同字体来编码。
-
大小写混合 :例如
baCoN->aabab(小写=a, 大写=b) -
AB字符混合 :例如
aabab(直接包含A/B或a/b字符)import re
class BaconCipher:
"""
培根密码解密工具
支持标准24字母表 (I/J, U/V 合并)
"""# 标准培根密码表 (I=J, U=V) # 注意:有些变体表会将 I/J 分开,U/V 分开,若解密乱码可尝试调整此处 BACON_DICT = { 'AAAAA': 'A', 'AAAAB': 'B', 'AAABA': 'C', 'AAABB': 'D', 'AABAA': 'E', 'AABAB': 'F', 'AABBA': 'G', 'AABBB': 'H', 'ABAAA': 'I', 'ABAAB': 'K', # I 对应 J 'ABABA': 'L', 'ABABB': 'M', 'ABBAA': 'N', 'ABBAB': 'O', 'ABBBA': 'P', 'ABBBB': 'Q', 'BAAAA': 'R', 'BAAAB': 'S', 'BAABA': 'T', 'BAABB': 'U', # U 对应 V 'BABAA': 'W', 'BABAB': 'X', 'BABBA': 'Y', 'BABBB': 'Z' } @staticmethod def decode(text, mode='case'): """ 解密培根密码 参数: text (str): 密文 mode (str): 解密模式 'case' - 大小写模式 (小写=a, 大写=b) 'char' - 字符模式 (a=A, b=B) 返回: str: 解密后的明文 """ # 1. 预处理:提取有效字符并转换为统一的 A/B 序列 ab_sequence = "" if mode == 'case': # 大小写模式:通常小写代表a,大写代表b (反之亦可,视题目而定) # 这里假设:小写->A, 大写->B for char in text: if char.islower(): ab_sequence += 'A' elif char.isupper(): ab_sequence += 'B' elif mode == 'char': # 字符模式:直接查找 a/A 和 b/B # 统一转为大写方便处理 clean_text = text.upper() for char in clean_text: if char == 'A': ab_sequence += 'A' elif char == 'B': ab_sequence += 'B' # 忽略其他字符 else: return "错误:未知的解密模式" # 2. 检查长度是否合法 (必须是5的倍数) if len(ab_sequence) % 5 != 0: print(f"警告:提取的序列长度为 {len(ab_sequence)},不是5的倍数,末尾多余字符将被忽略。") # 3. 每5位一组进行查表解密 result = "" for i in range(0, len(ab_sequence), 5): # 截取5个字符 chunk = ab_sequence[i:i+5] # 如果不足5个字符(也就是最后剩下的),则跳出 if len(chunk) < 5: break # 查表 letter = BaconCipher.BACON_DICT.get(chunk, '?') result += letter return result==========================================
使用示例
==========================================
if name == "main":
solver = BaconCipher()# 场景1:大小写混合 (CTF常见形式) # 密文示例:baCoNBacoN... (小写=a, 大写=b) cipher_case = "baCoNBacoNbaconbACoNbacOnbAconBacOnbacoNbaconbacOnbACOnbACoN" print(f"--- 场景1: 大小写模式 ---") print(f"密文: {cipher_case}") print(f"明文: {solver.decode(cipher_case, mode='case')}") print() # 场景2:AB字符混合 # 密文示例:AABBABABAA... cipher_char = "AABBABABAAABBBABABBAAAAAAABBABAABBA" print(f"--- 场景2: AB字符模式 ---") print(f"密文: {cipher_char}") print(f"明文: {solver.decode(cipher_char, mode='char')}")
3.6 其他古典密码简介
Playfair 密码: 使用一个5×5的字母矩阵(I和J合并),明文两两分组,根据字母在矩阵中的位置关系进行替换。规则:同行取右边、同列取下方、不同行列取对角。
Polybius 方阵密码: 使用5×5方阵,每个字母用其行列坐标表示。如A=11, B=12, ..., Z=55。
Atbash 密码: 字母表反转替换。A↔Z, B↔Y, C↔X, ..., M↔N。可看作凯撒密码的特殊形式。
class AtbashCipher:
"""
Atbash (埃特巴什码) 加解密工具
原理:字母表倒序映射 (A<->Z, B<->Y, ...)
特性:加密和解密使用同一算法 (自反性)
"""
# 定义希伯来字母表范围 (可选,用于处理特殊题目)
HEBREW_ALEPH = ord('א')
HEBREW_TAV = ord('ת')
@staticmethod
def process(text):
"""
执行 Atbash 变换 (加/解密)
参数:
text (str): 输入字符串
返回:
str: 变换后的字符串
"""
result = ""
for char in text:
# 1. 处理大写英文字母 (A-Z)
if 'A' <= char <= 'Z':
# 公式:Cipher = (25 - (Plain - A)) + A
# 简化为:Z - Plain + A
result += chr(ord('Z') - (ord(char) - ord('A')))
# 2. 处理小写英文字母 (a-z)
elif 'a' <= char <= 'z':
result += chr(ord('z') - (ord(char) - ord('a')))
# 3. 处理希伯来字母 (可选扩展)
# 范围:א (Aleph) 到 ת (Tav)
elif AtbashCipher.HEBREW_ALEPH <= ord(char) <= AtbashCipher.HEBREW_TAV:
result += chr(AtbashCipher.HEBREW_TAV - (ord(char) - AtbashCipher.HEBREW_ALEPH))
# 4. 其他字符保持不变 (数字、标点、空格)
else:
result += char
return result
@staticmethod
def solve_ctf_challenge(ciphertext):
"""
专门用于 CTF 的快捷方法
通常 CTF 题目只需要处理英文部分
"""
return AtbashCipher.process(ciphertext)
# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
solver = AtbashCipher()
# 场景1:标准英文句子
text1 = "the quick brown fox jumps over the lazy dog"
print(f"--- 场景1: 英文句子 ---")
print(f"原文: {text1}")
print(f"密文: {solver.process(text1)}")
print(f"回解密: {solver.process(solver.process(text1))}") # 验证自反性
print()
# 场景2:CTF Flag 格式
# 假设题目给出 flag{...} 的变体,或者全大写的密文
cipher_flag = "uozt{Zgyzhv_xlwv_uiln_xguhsld}"
print(f"--- 场景2: CTF Flag ---")
print(f"密文: {cipher_flag}")
print(f"明文: {solver.solve_ctf_challenge(cipher_flag)}")
# 预期输出: flag{Atbash_is_very_easy_to_break} (仅为示例推测)
print()
# 场景3:混合字符
text3 = "Hello World! 123"
print(f"--- 场景3: 混合字符 ---")
print(f"原文: {text3}")
print(f"密文: {solver.process(text3)}")
ROT13 : 凯撒密码偏移量为13的特例。由于英文字母共26个,ROT13加密两次回到原文,即加密和解密操作相同。
猪圈密码( Pigpen Cipher ): 一种图形替换密码,每个字母对应一个由线段和点组成的独特图形。需要查阅对应的图形映射表进行解密。在CTF中,通常以图片形式出题。
3.7 综合实战案例
|-------------------------------------------------------------------------------|
| 📋 题目 你截获了以下密文:Gur synt vf: synt{pynffvpny_pelcgb_vf_sha} 请分析密码类型并破解。 |
解题思路:
-
观察特征: 密文保留了空格和标点,字母被替换但结构不变,长度与原文一致------这是一种单表替换密码
-
关键线索: "synt" 出现两次,且前面有 "synt vf:" 的模式,很像 "flag is: flag{...}"
-
验证: s→f (偏移13), y→l (偏移13), n→a (偏移13), t→g (偏移13) --- 偏移量恒为13
-
确认: 这是ROT13加密
import codecs
import stringclass ROT13Solver:
"""
ROT13 加解密工具
原理:凯撒密码的特例,位移量为 13
特性:自反性 (ROT13(ROT13(x)) = x)
"""@staticmethod def solve_standard(text): """ 方法一:使用 codecs 标准库 (推荐,最稳健) 适用于:绝大多数 CTF 题目 """ return codecs.decode(text, 'rot_13') @staticmethod def solve_manual(text): """ 方法二:手动实现 (理解原理) 适用于:不允许使用标准库的受限环境或学习目的 """ result = "" for char in text: if char.islower(): # 小写字母处理:(当前索引 + 13) % 26 result += chr((ord(char) - ord('a') + 13) % 26 + ord('a')) elif char.isupper(): # 大写字母处理 result += chr((ord(char) - ord('A') + 13) % 26 + ord('A')) else: # 非字母字符保持不变 (数字、标点、花括号等) result += char return result @staticmethod def solve_translate(text): """ 方法三:字符串映射 (高性能) 适用于:需要处理超大文本或追求极致速度的场景 """ # 构建转换表:ABCDEFGHIJKLMNOPQRSTUVWXYZ -> NOPQRSTUVWXYZABCDEFGHIJKLM trans_table = str.maketrans( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm" ) return text.translate(trans_table) @staticmethod def extract_flag(ciphertext): """ CTF 专用:尝试解密并提取 Flag """ plaintext = ROT13Solver.solve_standard(ciphertext) if "flag{" in plaintext: # 简单提取 flag{...} 内容 start = plaintext.find("flag{") end = plaintext.find("}", start) if end != -1: return plaintext[start:end+1] return plaintext==========================================
使用示例
==========================================
if name == "main":
solver = ROT13Solver()# 场景1:基础解密 (你的原始代码场景) ciphertext = "Gur synt vf: synt{pynffvpny_pelcgb_vf_sha}" print(f"--- 场景1: 基础解密 ---") print(f"密文: {ciphertext}") print(f"解密: {solver.solve_standard(ciphertext)}") # 输出: The flag is: flag{classical_crypto_is_fun} print() # 场景2:验证自反性 (加密=解密) original = "Hello CTF" encrypted = solver.solve_standard(original) decrypted = solver.solve_standard(encrypted) print(f"--- 场景2: 自反性验证 ---") print(f"原文: {original}") print(f"加密: {encrypted}") # Uryyb PSG print(f"解密: {decrypted}") # Hello CTF print() # 场景3:CTF Flag 提取 messy_text = "Uryyb Jbeyq! Gur synt vf synt{ebg13_vf_fvzcyr}." flag = solver.extract_flag(messy_text) print(f"--- 场景3: Flag 提取 ---") print(f"杂讯文本: {messy_text}") print(f"提取结果: {flag}")