classical-cipher-analysis
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: Classical Cipher Analysis — Expert Cryptanalysis Playbook
技能:古典密码分析 — 专业密码分析手册
AI LOAD INSTRUCTION: Expert classical cipher identification and breaking techniques for CTF. Covers cipher identification methodology (frequency analysis, IC, Kasiski), monoalphabetic substitution, Caesar/ROT, Vigenere, Enigma, affine, Hill, transposition ciphers, Bacon/Polybius/Playfair, and XOR ciphers. Base models often skip the identification step and jump to the wrong cipher type, or fail to recognize encoded (base64/hex) ciphertext that needs decoding before analysis.
AI加载说明:适用于CTF的专业古典密码识别与破解技术。涵盖密码识别方法论(频率分析、IC、Kasiski检验)、单字母替换密码、Caesar/ROT、Vigenere、Enigma、仿射密码、Hill密码、换位密码、Bacon/Polybius/Playfair密码以及XOR密码。基础模型通常会跳过识别步骤直接判定为错误的密码类型,或者无法识别需要先解码的(Base64/十六进制)密文。
0. RELATED ROUTING
0. 相关跳转指引
- symmetric-cipher-attacks when dealing with modern symmetric ciphers (AES/DES) rather than classical
- hash-attack-techniques when the challenge involves hash-based constructions
- lattice-crypto-attacks when knapsack-based ciphers are encountered
- 处理现代对称密码(AES/DES)而非古典密码时,参考对称密码攻击
- 挑战涉及哈希类构造时,参考哈希攻击技术
- 遇到基于背包的密码时,参考格密码攻击
Quick identification guide
快速识别指南
| Observation | Likely Cipher | First Action |
|---|---|---|
| All uppercase letters, uneven frequency | Monoalphabetic substitution | Frequency analysis |
| All uppercase, flat frequency distribution | Polyalphabetic (Vigenere) | IC + Kasiski |
| Only A-Z shifted uniformly | Caesar/ROT | Brute force 25 shifts |
| Base64 alphabet (A-Za-z0-9+/=) | Base64 encoded (decode first) | Base64 decode |
| Hex string (0-9a-f) | Hex encoded (decode first) | Hex decode |
| Binary (0s and 1s) | Binary encoded | Convert to ASCII |
| Dots and dashes | Morse code | Morse decode |
| Raised/normal text pattern | Bacon cipher | Map to A/B, decode |
| 2-digit number pairs (11-55) | Polybius square | Grid lookup |
| Text appears scrambled (right letters, wrong order) | Transposition | Anagram analysis |
| Non-printable bytes XOR-like | XOR cipher | Single/repeating key XOR analysis |
| 观察特征 | 可能的密码类型 | 第一步操作 |
|---|---|---|
| 全大写字母,频率分布不均 | 单字母替换密码 | 频率分析 |
| 全大写,频率分布平坦 | 多字母替换(Vigenere) | IC计算 + Kasiski检验 |
| 仅A-Z统一偏移 | Caesar/ROT | 暴力尝试25种偏移 |
| Base64字符集(A-Za-z0-9+/=) | Base64编码(需先解码) | Base64解码 |
| 十六进制字符串(0-9a-f) | 十六进制编码(需先解码) | 十六进制解码 |
| 二进制(仅0和1) | 二进制编码 | 转换为ASCII |
| 点和横杠 | 摩尔斯电码 | 摩尔斯解码 |
| 大小写/正常与上标文本模式 | Bacon密码 | 映射为A/B后解码 |
| 两位数字对(11-55) | Polybius方格 | 网格查表 |
| 文本看起来被打乱(字母正确,顺序错误) | 换位密码 | 变位词分析 |
| 非打印字节,类XOR特征 | XOR密码 | 单字节/重复密钥XOR分析 |
1. CIPHER IDENTIFICATION METHODOLOGY
1. 密码识别方法论
1.1 Step 1: Character Set Analysis
1.1 步骤1:字符集分析
python
def analyze_charset(ciphertext):
"""Identify encoding/cipher by character set."""
chars = set(ciphertext.strip())
if chars <= set('01 \n'):
return "Binary encoding"
if chars <= set('.-/ \n'):
return "Morse code"
if chars <= set('0123456789abcdef \n'):
return "Hex encoding"
if chars <= set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n'):
if '=' in ciphertext or len(ciphertext) % 4 == 0:
return "Base64 encoding"
if chars <= set('ABCDEFGHIJKLMNOPQRSTUVWXYZ \n'):
return "Uppercase only — classical cipher"
if all(c in '12345' for c in ciphertext.replace(' ', '').replace('\n', '')):
return "Polybius square (digits 1-5)"
return "Mixed charset — needs further analysis"python
def analyze_charset(ciphertext):
"""Identify encoding/cipher by character set."""
chars = set(ciphertext.strip())
if chars <= set('01 \n'):
return "Binary encoding"
if chars <= set('.-/ \n'):
return "Morse code"
if chars <= set('0123456789abcdef \n'):
return "Hex encoding"
if chars <= set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n'):
if '=' in ciphertext or len(ciphertext) % 4 == 0:
return "Base64 encoding"
if chars <= set('ABCDEFGHIJKLMNOPQRSTUVWXYZ \n'):
return "Uppercase only — classical cipher"
if all(c in '12345' for c in ciphertext.replace(' ', '').replace('\n', '')):
return "Polybius square (digits 1-5)"
return "Mixed charset — needs further analysis"1.2 Step 2: Frequency Analysis
1.2 步骤2:频率分析
python
from collections import Counter
def frequency_analysis(text):
"""Compute letter frequency distribution."""
text = text.upper()
letters = [c for c in text if c.isalpha()]
total = len(letters)
freq = Counter(letters)
print("Letter frequencies:")
for letter, count in freq.most_common():
pct = count / total * 100
bar = '#' * int(pct)
print(f" {letter}: {pct:5.1f}% {bar}")
return freqpython
from collections import Counter
def frequency_analysis(text):
"""Compute letter frequency distribution."""
text = text.upper()
letters = [c for c in text if c.isalpha()]
total = len(letters)
freq = Counter(letters)
print("Letter frequencies:")
for letter, count in freq.most_common():
pct = count / total * 100
bar = '#' * int(pct)
print(f" {letter}: {pct:5.1f}% {bar}")
return freqEnglish letter frequency (for comparison):
English letter frequency (for comparison):
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
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
12.7 9.1 8.2 7.5 7.0 6.7 6.3 6.1 6.0 4.3 4.0 2.8 ...
12.7 9.1 8.2 7.5 7.0 6.7 6.3 6.1 6.0 4.3 4.0 2.8 ...
undefinedundefined1.3 Step 3: Index of Coincidence (IC)
1.3 步骤3:重合指数(IC)
python
def index_of_coincidence(text):
"""
IC ≈ 0.065 → English / monoalphabetic substitution
IC ≈ 0.038 → random / polyalphabetic cipher
"""
text = [c for c in text.upper() if c.isalpha()]
N = len(text)
freq = Counter(text)
ic = sum(f * (f - 1) for f in freq.values()) / (N * (N - 1))
return icpython
def index_of_coincidence(text):
"""
IC ≈ 0.065 → English / monoalphabetic substitution
IC ≈ 0.038 → random / polyalphabetic cipher
"""
text = [c for c in text.upper() if c.isalpha()]
N = len(text)
freq = Counter(text)
ic = sum(f * (f - 1) for f in freq.values()) / (N * (N - 1))
return icInterpretation:
Interpretation:
IC > 0.060 → monoalphabetic (Caesar, simple substitution, Playfair)
IC > 0.060 → monoalphabetic (Caesar, simple substitution, Playfair)
IC ≈ 0.045-0.055 → polyalphabetic with short key (Vigenere key < 10)
IC ≈ 0.045-0.055 → polyalphabetic with short key (Vigenere key < 10)
IC ≈ 0.038-0.042 → polyalphabetic with long key or random
IC ≈ 0.038-0.042 → polyalphabetic with long key or random
undefinedundefined1.4 Step 4: Kasiski Examination (for Polyalphabetic)
1.4 步骤4:Kasiski检验(适用于多字母替换密码)
python
from math import gcd
from functools import reduce
def kasiski(ciphertext, min_len=3):
"""Find repeated sequences and their distances → key length."""
text = ''.join(c for c in ciphertext.upper() if c.isalpha())
distances = []
for length in range(min_len, min(20, len(text) // 3)):
for i in range(len(text) - length):
seq = text[i:i+length]
j = text.find(seq, i + 1)
while j != -1:
distances.append(j - i)
j = text.find(seq, j + 1)
if not distances:
return None
# Key length is likely GCD of common distances
common_gcds = Counter()
for d in distances:
for factor in range(2, min(d + 1, 30)):
if d % factor == 0:
common_gcds[factor] += 1
print("Likely key lengths (by frequency):")
for length, count in common_gcds.most_common(5):
print(f" Key length {length}: {count} occurrences")
return common_gcds.most_common(1)[0][0]python
from math import gcd
from functools import reduce
def kasiski(ciphertext, min_len=3):
"""Find repeated sequences and their distances → key length."""
text = ''.join(c for c in ciphertext.upper() if c.isalpha())
distances = []
for length in range(min_len, min(20, len(text) // 3)):
for i in range(len(text) - length):
seq = text[i:i+length]
j = text.find(seq, i + 1)
while j != -1:
distances.append(j - i)
j = text.find(seq, j + 1)
if not distances:
return None
# Key length is likely GCD of common distances
common_gcds = Counter()
for d in distances:
for factor in range(2, min(d + 1, 30)):
if d % factor == 0:
common_gcds[factor] += 1
print("Likely key lengths (by frequency):")
for length, count in common_gcds.most_common(5):
print(f" Key length {length}: {count} occurrences")
return common_gcds.most_common(1)[0][0]2. MONOALPHABETIC SUBSTITUTION
2. 单字母替换密码
2.1 Frequency Analysis Attack
2.1 频率分析攻击
python
def solve_substitution(ciphertext, interactive=False):
"""Solve monoalphabetic substitution via frequency analysis."""
freq = frequency_analysis(ciphertext)
# English frequency order
eng_order = "ETAOINSRHLDCUMWFGYPBVKJXQZ"
cipher_order = ''.join(c for c, _ in freq.most_common())
# Initial mapping (frequency-based guess)
mapping = {}
for i, c in enumerate(cipher_order):
if i < len(eng_order):
mapping[c] = eng_order[i]
# Apply mapping
result = ""
for c in ciphertext.upper():
result += mapping.get(c, c)
return result, mappingpython
def solve_substitution(ciphertext, interactive=False):
"""Solve monoalphabetic substitution via frequency analysis."""
freq = frequency_analysis(ciphertext)
# English frequency order
eng_order = "ETAOINSRHLDCUMWFGYPBVKJXQZ"
cipher_order = ''.join(c for c, _ in freq.most_common())
# Initial mapping (frequency-based guess)
mapping = {}
for i, c in enumerate(cipher_order):
if i < len(eng_order):
mapping[c] = eng_order[i]
# Apply mapping
result = ""
for c in ciphertext.upper():
result += mapping.get(c, c)
return result, mappingBetter approach: use automated solvers
Better approach: use automated solvers
quipqiup.com — online substitution solver
quipqiup.com — online substitution solver
dcode.fr/monoalphabetic-substitution — with word pattern matching
dcode.fr/monoalphabetic-substitution — with word pattern matching
undefinedundefined2.2 Known Plaintext (Crib Dragging)
2.2 已知明文攻击(Crib Dragging)
If part of the plaintext is known (e.g., "flag{" prefix):
python
def crib_drag_substitution(ciphertext, known_plain, known_cipher):
"""Build partial mapping from known plaintext-ciphertext pair."""
mapping = {}
for p, c in zip(known_plain.upper(), known_cipher.upper()):
mapping[c] = p
# Apply partial mapping
result = ""
for c in ciphertext.upper():
result += mapping.get(c, '?')
return result, mapping如果已知部分明文(例如前缀"flag{"):
python
def crib_drag_substitution(ciphertext, known_plain, known_cipher):
"""Build partial mapping from known plaintext-ciphertext pair."""
mapping = {}
for p, c in zip(known_plain.upper(), known_cipher.upper()):
mapping[c] = p
# Apply partial mapping
result = ""
for c in ciphertext.upper():
result += mapping.get(c, '?')
return result, mapping3. CAESAR / ROT CIPHERS
3. CAESAR / ROT密码
3.1 Brute Force
3.1 暴力破解
python
def caesar_bruteforce(ciphertext):
"""Try all 25 shifts, score by English frequency."""
results = []
for shift in range(26):
decrypted = ""
for c in ciphertext:
if c.isalpha():
base = ord('A') if c.isupper() else ord('a')
decrypted += chr((ord(c) - base - shift) % 26 + base)
else:
decrypted += c
# Chi-squared scoring against English frequency
score = chi_squared_score(decrypted)
results.append((shift, score, decrypted))
results.sort(key=lambda x: x[1])
return results[0] # best match
def chi_squared_score(text):
"""Lower score = closer to English."""
expected = {
'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, 'C': 2.8, 'U': 2.8, 'M': 2.4, 'W': 2.4,
'F': 2.2, 'G': 2.0, 'Y': 2.0, 'P': 1.9, 'B': 1.5,
'V': 1.0, 'K': 0.8, 'J': 0.2, 'X': 0.2, 'Q': 0.1, 'Z': 0.1,
}
text = text.upper()
letters = [c for c in text if c.isalpha()]
total = len(letters)
if total == 0:
return float('inf')
freq = Counter(letters)
score = sum(
(freq.get(c, 0) / total * 100 - expected.get(c, 0)) ** 2 / max(expected.get(c, 0.1), 0.1)
for c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
)
return scorepython
def caesar_bruteforce(ciphertext):
"""Try all 25 shifts, score by English frequency."""
results = []
for shift in range(26):
decrypted = ""
for c in ciphertext:
if c.isalpha():
base = ord('A') if c.isupper() else ord('a')
decrypted += chr((ord(c) - base - shift) % 26 + base)
else:
decrypted += c
# Chi-squared scoring against English frequency
score = chi_squared_score(decrypted)
results.append((shift, score, decrypted))
results.sort(key=lambda x: x[1])
return results[0] # best match
def chi_squared_score(text):
"""Lower score = closer to English."""
expected = {
'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, 'C': 2.8, 'U': 2.8, 'M': 2.4, 'W': 2.4,
'F': 2.2, 'G': 2.0, 'Y': 2.0, 'P': 1.9, 'B': 1.5,
'V': 1.0, 'K': 0.8, 'J': 0.2, 'X': 0.2, 'Q': 0.1, 'Z': 0.1,
}
text = text.upper()
letters = [c for c in text if c.isalpha()]
total = len(letters)
if total == 0:
return float('inf')
freq = Counter(letters)
score = sum(
(freq.get(c, 0) / total * 100 - expected.get(c, 0)) ** 2 / max(expected.get(c, 0.1), 0.1)
for c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
)
return score3.2 ROT13 and ROT47
3.2 ROT13 和 ROT47
python
import codecspython
import codecsROT13 (letters only)
ROT13 (仅字母)
rot13 = codecs.decode(ciphertext, 'rot_13')
rot13 = codecs.decode(ciphertext, 'rot_13')
ROT47 (ASCII 33-126)
ROT47 (ASCII 33-126)
def rot47(text):
return ''.join(
chr(33 + (ord(c) - 33 + 47) % 94) if 33 <= ord(c) <= 126 else c
for c in text
)
---def rot47(text):
return ''.join(
chr(33 + (ord(c) - 33 + 47) % 94) if 33 <= ord(c) <= 126 else c
for c in text
)
---4. VIGENERE CIPHER
4. VIGENERE密码
4.1 Full Attack Workflow
4.1 完整攻击流程
Step 1: Confirm polyalphabetic (IC ≈ 0.04-0.05)
Step 2: Find key length (Kasiski + IC per period)
Step 3: For each key position, solve as single Caesar cipher
Step 4: Assemble key → decrypt步骤1:确认是多字母替换(IC ≈ 0.04-0.05)
步骤2:计算密钥长度(Kasiski检验 + 按周期计算IC)
步骤3:对每个密钥位置,当作单Caesar密码破解
步骤4:拼接密钥 → 解密4.2 IC-Based Key Length Detection
4.2 基于IC的密钥长度检测
python
def find_vigenere_key_length(ciphertext, max_key=20):
"""Use IC to find Vigenere key length."""
text = [c for c in ciphertext.upper() if c.isalpha()]
results = []
for kl in range(1, max_key + 1):
# Split text into kl columns
columns = [[] for _ in range(kl)]
for i, c in enumerate(text):
columns[i % kl].append(c)
# Average IC across columns
avg_ic = sum(
index_of_coincidence(''.join(col)) for col in columns
) / kl
results.append((kl, avg_ic))
print(f" Key length {kl:2d}: IC = {avg_ic:.4f}")
# Key length with IC closest to 0.065
best = max(results, key=lambda x: x[1])
return best[0]python
def find_vigenere_key_length(ciphertext, max_key=20):
"""Use IC to find Vigenere key length."""
text = [c for c in ciphertext.upper() if c.isalpha()]
results = []
for kl in range(1, max_key + 1):
# Split text into kl columns
columns = [[] for _ in range(kl)]
for i, c in enumerate(text):
columns[i % kl].append(c)
# Average IC across columns
avg_ic = sum(
index_of_coincidence(''.join(col)) for col in columns
) / kl
results.append((kl, avg_ic))
print(f" Key length {kl:2d}: IC = {avg_ic:.4f}")
# Key length with IC closest to 0.065
best = max(results, key=lambda x: x[1])
return best[0]4.3 Per-Position Frequency Attack
4.3 按位置频率攻击
python
def crack_vigenere(ciphertext, key_length):
"""Crack Vigenere given known key length."""
text = [c for c in ciphertext.upper() if c.isalpha()]
key = ""
for pos in range(key_length):
column = ''.join(text[i] for i in range(pos, len(text), key_length))
# Solve as Caesar cipher
shift, score, _ = caesar_bruteforce(column)
key += chr(shift + ord('A'))
# Decrypt
plaintext = ""
ki = 0
for c in ciphertext:
if c.isalpha():
shift = ord(key[ki % key_length]) - ord('A')
base = ord('A') if c.isupper() else ord('a')
plaintext += chr((ord(c) - base - shift) % 26 + base)
ki += 1
else:
plaintext += c
return key, plaintextpython
def crack_vigenere(ciphertext, key_length):
"""Crack Vigenere given known key length."""
text = [c for c in ciphertext.upper() if c.isalpha()]
key = ""
for pos in range(key_length):
column = ''.join(text[i] for i in range(pos, len(text), key_length))
# Solve as Caesar cipher
shift, score, _ = caesar_bruteforce(column)
key += chr(shift + ord('A'))
# Decrypt
plaintext = ""
ki = 0
for c in ciphertext:
if c.isalpha():
shift = ord(key[ki % key_length]) - ord('A')
base = ord('A') if c.isupper() else ord('a')
plaintext += chr((ord(c) - base - shift) % 26 + base)
ki += 1
else:
plaintext += c
return key, plaintext5. AFFINE CIPHER
5. 仿射密码
5.1 Definition
5.1 定义
E(x) = (a·x + b) mod 26Valid a values: 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25 (12 values).
E(x) = (a·x + b) mod 26有效a值:1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25(共12个值)。
5.2 Brute Force (312 combinations)
5.2 暴力破解(312种组合)
python
def crack_affine(ciphertext):
"""Brute force affine cipher: 12 × 26 = 312 combinations."""
valid_a = [a for a in range(1, 26) if gcd(a, 26) == 1]
for a in valid_a:
a_inv = pow(a, -1, 26)
for b in range(26):
plaintext = ""
for c in ciphertext.upper():
if c.isalpha():
y = ord(c) - ord('A')
x = (a_inv * (y - b)) % 26
plaintext += chr(x + ord('A'))
else:
plaintext += c
score = chi_squared_score(plaintext)
if score < 50: # reasonable English
print(f"a={a}, b={b}: {plaintext[:50]}...")python
def crack_affine(ciphertext):
"""Brute force affine cipher: 12 × 26 = 312 combinations."""
valid_a = [a for a in range(1, 26) if gcd(a, 26) == 1]
for a in valid_a:
a_inv = pow(a, -1, 26)
for b in range(26):
plaintext = ""
for c in ciphertext.upper():
if c.isalpha():
y = ord(c) - ord('A')
x = (a_inv * (y - b)) % 26
plaintext += chr(x + ord('A'))
else:
plaintext += c
score = chi_squared_score(plaintext)
if score < 50: # reasonable English
print(f"a={a}, b={b}: {plaintext[:50]}...")5.3 Known Plaintext
5.3 已知明文攻击
python
def affine_from_known(plain1, cipher1, plain2, cipher2):
"""Recover (a, b) from two known plaintext-ciphertext pairs."""
p1, c1 = ord(plain1) - ord('A'), ord(cipher1) - ord('A')
p2, c2 = ord(plain2) - ord('A'), ord(cipher2) - ord('A')
# c1 = a*p1 + b, c2 = a*p2 + b
# c1 - c2 = a*(p1 - p2) mod 26
diff_p = (p1 - p2) % 26
diff_c = (c1 - c2) % 26
if gcd(diff_p, 26) != 1:
return None
a = (diff_c * pow(diff_p, -1, 26)) % 26
b = (c1 - a * p1) % 26
return a, bpython
def affine_from_known(plain1, cipher1, plain2, cipher2):
"""Recover (a, b) from two known plaintext-ciphertext pairs."""
p1, c1 = ord(plain1) - ord('A'), ord(cipher1) - ord('A')
p2, c2 = ord(plain2) - ord('A'), ord(cipher2) - ord('A')
# c1 = a*p1 + b, c2 = a*p2 + b
# c1 - c2 = a*(p1 - p2) mod 26
diff_p = (p1 - p2) % 26
diff_c = (c1 - c2) % 26
if gcd(diff_p, 26) != 1:
return None
a = (diff_c * pow(diff_p, -1, 26)) % 26
b = (c1 - a * p1) % 26
return a, b6. HILL CIPHER
6. HILL密码
Matrix-based cipher: where K is an n×n key matrix.
C = K · P mod 26基于矩阵的密码:,其中K是n×n的密钥矩阵。
C = K · P mod 266.1 Known-Plaintext Attack
6.1 已知明文攻击
python
import numpy as np
def crack_hill(known_plain, known_cipher, n=2):
"""Recover Hill cipher key from known plaintext-ciphertext (mod 26)."""
# Convert to numbers
P = [ord(c) - ord('A') for c in known_plain.upper()]
C = [ord(c) - ord('A') for c in known_cipher.upper()]
# Build matrices (need at least n pairs of n-grams)
P_matrix = np.array(P[:n*n]).reshape(n, n).T
C_matrix = np.array(C[:n*n]).reshape(n, n).T
# K = C · P⁻¹ mod 26
# Need modular matrix inverse
from sympy import Matrix
P_mat = Matrix(P_matrix.tolist())
C_mat = Matrix(C_matrix.tolist())
P_inv = P_mat.inv_mod(26)
K = (C_mat * P_inv) % 26
return Kpython
import numpy as np
def crack_hill(known_plain, known_cipher, n=2):
"""Recover Hill cipher key from known plaintext-ciphertext (mod 26)."""
# Convert to numbers
P = [ord(c) - ord('A') for c in known_plain.upper()]
C = [ord(c) - ord('A') for c in known_cipher.upper()]
# Build matrices (need at least n pairs of n-grams)
P_matrix = np.array(P[:n*n]).reshape(n, n).T
C_matrix = np.array(C[:n*n]).reshape(n, n).T
# K = C · P⁻¹ mod 26
# Need modular matrix inverse
from sympy import Matrix
P_mat = Matrix(P_matrix.tolist())
C_mat = Matrix(C_matrix.tolist())
P_inv = P_mat.inv_mod(26)
K = (C_mat * P_inv) % 26
return K7. TRANSPOSITION CIPHERS
7. 换位密码
7.1 Rail Fence
7.1 栅栏密码
python
def rail_fence_decrypt(ciphertext, rails):
"""Decrypt rail fence cipher."""
n = len(ciphertext)
# Build the zigzag pattern
pattern = []
for i in range(n):
row = 0
cycle = 2 * (rails - 1)
pos = i % cycle
row = pos if pos < rails else cycle - pos
pattern.append((row, i))
pattern.sort()
# Fill in characters
result = [''] * n
ci = 0
for _, orig_pos in pattern:
result[orig_pos] = ciphertext[ci]
ci += 1
return ''.join(result)python
def rail_fence_decrypt(ciphertext, rails):
"""Decrypt rail fence cipher."""
n = len(ciphertext)
# Build the zigzag pattern
pattern = []
for i in range(n):
row = 0
cycle = 2 * (rails - 1)
pos = i % cycle
row = pos if pos < rails else cycle - pos
pattern.append((row, i))
pattern.sort()
# Fill in characters
result = [''] * n
ci = 0
for _, orig_pos in pattern:
result[orig_pos] = ciphertext[ci]
ci += 1
return ''.join(result)Brute force all rail counts
Brute force all rail counts
for rails in range(2, 20):
print(f"Rails {rails}: {rail_fence_decrypt(ct, rails)[:50]}")
undefinedfor rails in range(2, 20):
print(f"Rails {rails}: {rail_fence_decrypt(ct, rails)[:50]}")
undefined7.2 Columnar Transposition
7.2 列换位密码
python
def columnar_decrypt(ciphertext, key):
"""Decrypt columnar transposition given key word."""
n_cols = len(key)
n_rows = -(-len(ciphertext) // n_cols) # ceiling division
# Determine column order from key
order = sorted(range(n_cols), key=lambda i: key[i])
# Calculate column lengths (some may be shorter)
full_cols = len(ciphertext) % n_cols
if full_cols == 0:
full_cols = n_cols
# Split ciphertext into columns (in key order)
columns = [''] * n_cols
pos = 0
for col_idx in order:
col_len = n_rows if col_idx < full_cols else n_rows - 1
columns[col_idx] = ciphertext[pos:pos + col_len]
pos += col_len
# Read off row by row
plaintext = ''
for row in range(n_rows):
for col in range(n_cols):
if row < len(columns[col]):
plaintext += columns[col][row]
return plaintextpython
def columnar_decrypt(ciphertext, key):
"""Decrypt columnar transposition given key word."""
n_cols = len(key)
n_rows = -(-len(ciphertext) // n_cols) # ceiling division
# Determine column order from key
order = sorted(range(n_cols), key=lambda i: key[i])
# Calculate column lengths (some may be shorter)
full_cols = len(ciphertext) % n_cols
if full_cols == 0:
full_cols = n_cols
# Split ciphertext into columns (in key order)
columns = [''] * n_cols
pos = 0
for col_idx in order:
col_len = n_rows if col_idx < full_cols else n_rows - 1
columns[col_idx] = ciphertext[pos:pos + col_len]
pos += col_len
# Read off row by row
plaintext = ''
for row in range(n_rows):
for col in range(n_cols):
if row < len(columns[col]):
plaintext += columns[col][row]
return plaintext8. XOR CIPHER
8. XOR密码
8.1 Single-Byte XOR
8.1 单字节XOR
See symmetric-cipher-attacks Section 4.2 for full implementation.
完整实现参考对称密码攻击第4.2节。
8.2 Multi-Byte XOR (xortool)
8.2 多字节XOR(xortool)
bash
undefinedbash
undefinedAutomatic key length detection and cracking
自动密钥长度检测与破解
xortool ciphertext.bin -l 5 # try key length 5
xortool ciphertext.bin -b # brute force key length
xortool ciphertext.bin -c 20 # assume most common char is space (0x20)
undefinedxortool ciphertext.bin -l 5 # 尝试密钥长度5
xortool ciphertext.bin -b # 暴力枚举密钥长度
xortool ciphertext.bin -c 20 # 假设最常见字符是空格(0x20)
undefined8.3 Known Plaintext XOR
8.3 已知明文XOR攻击
python
def xor_known_plaintext(ciphertext, known_plain, offset=0):
"""Recover XOR key from known plaintext at given offset."""
key_fragment = bytes(
c ^ p for c, p in zip(ciphertext[offset:], known_plain)
)
print(f"Key fragment: {key_fragment}")
# If repeating key, infer full key from fragment
return key_fragmentpython
def xor_known_plaintext(ciphertext, known_plain, offset=0):
"""Recover XOR key from known plaintext at given offset."""
key_fragment = bytes(
c ^ p for c, p in zip(ciphertext[offset:], known_plain)
)
print(f"Key fragment: {key_fragment}")
# If repeating key, infer full key from fragment
return key_fragment9. SPECIAL CIPHERS
9. 特殊密码
9.1 Bacon Cipher
9.1 Bacon密码
Binary encoding using two typefaces (A=normal, B=bold/italic).
python
BACON = {
'AAAAA': 'A', 'AAAAB': 'B', 'AAABA': 'C', 'AAABB': 'D',
'AABAA': 'E', 'AABAB': 'F', 'AABBA': 'G', 'AABBB': 'H',
'ABAAA': 'I', 'ABAAB': 'J', 'ABABA': 'K', 'ABABB': 'L',
'ABBAA': 'M', 'ABBAB': 'N', 'ABBBA': 'O', 'ABBBB': 'P',
'BAAAA': 'Q', 'BAAAB': 'R', 'BAABA': 'S', 'BAABB': 'T',
'BABAA': 'U', 'BABAB': 'V', 'BABBA': 'W', 'BABBB': 'X',
'BAAAA': 'Y', 'BAAAB': 'Z',
}
def decode_bacon(text):
"""Decode Bacon cipher: uppercase=B, lowercase=A (or similar mapping)."""
binary = ''.join('B' if c.isupper() else 'A' for c in text if c.isalpha())
result = ''
for i in range(0, len(binary) - 4, 5):
chunk = binary[i:i+5]
result += BACON.get(chunk, '?')
return result使用两种字体的二进制编码(A=普通,B=加粗/斜体)。
python
BACON = {
'AAAAA': 'A', 'AAAAB': 'B', 'AAABA': 'C', 'AAABB': 'D',
'AABAA': 'E', 'AABAB': 'F', 'AABBA': 'G', 'AABBB': 'H',
'ABAAA': 'I', 'ABAAB': 'J', 'ABABA': 'K', 'ABABB': 'L',
'ABBAA': 'M', 'ABBAB': 'N', 'ABBBA': 'O', 'ABBBB': 'P',
'BAAAA': 'Q', 'BAAAB': 'R', 'BAABA': 'S', 'BAABB': 'T',
'BABAA': 'U', 'BABAB': 'V', 'BABBA': 'W', 'BABBB': 'X',
'BAAAA': 'Y', 'BAAAB': 'Z',
}
def decode_bacon(text):
"""Decode Bacon cipher: uppercase=B, lowercase=A (or similar mapping)."""
binary = ''.join('B' if c.isupper() else 'A' for c in text if c.isalpha())
result = ''
for i in range(0, len(binary) - 4, 5):
chunk = binary[i:i+5]
result += BACON.get(chunk, '?')
return result9.2 Polybius Square
9.2 Polybius方格
1 2 3 4 5
┌──────────
1 │ A B C D E
2 │ F G H I/J K
3 │ L M N O P
4 │ Q R S T U
5 │ V W X Y Z
"HELLO" = "23 15 31 31 34" 1 2 3 4 5
┌──────────
1 │ A B C D E
2 │ F G H I/J K
3 │ L M N O P
4 │ Q R S T U
5 │ V W X Y Z
"HELLO" = "23 15 31 31 34"9.3 Playfair
9.3 Playfair密码
5×5 grid cipher encrypting digraphs.
Key: "MONARCHY" → grid:
M O N A R
C H Y B D
E F G I/J K
L P Q S T
U V W X Z
Rules:
Same row → shift right: HE → FE → "GF"
Same col → shift down
Rectangle → swap columns5×5网格密码,对双字母组加密。
密钥: "MONARCHY" → 网格:
M O N A R
C H Y B D
E F G I/J K
L P Q S T
U V W X Z
规则:
同行 → 右移: HE → FE → "GF"
同列 → 下移
矩形 → 交换列10. DECISION TREE
10. 决策树
Unknown ciphertext — how to identify and break?
│
├─ Step 1: Check encoding
│ ├─ Base64 alphabet with padding? → Decode first, then re-analyze
│ ├─ Hex string? → Convert to bytes, re-analyze
│ ├─ Binary (01)? → Convert to ASCII
│ ├─ Morse (.-/)? → Decode Morse
│ └─ Printable text? → Continue to Step 2
│
├─ Step 2: Character set
│ ├─ Only letters (A-Z)?
│ │ ├─ Compute IC
│ │ │ ├─ IC ≈ 0.065 → Monoalphabetic
│ │ │ │ ├─ Uniform shift in freq? → Caesar → brute force 25
│ │ │ │ ├─ Random-looking mapping? → Simple substitution → frequency analysis
│ │ │ │ └─ Digraph patterns? → Playfair → digraph analysis
│ │ │ │
│ │ │ ├─ IC ≈ 0.04-0.05 → Polyalphabetic
│ │ │ │ ├─ Kasiski → find key length
│ │ │ │ └─ Per-position frequency → crack Vigenere
│ │ │ │
│ │ │ └─ IC ≈ 0.038 → Very long key or one-time pad
│ │ │ └─ Look for key reuse or weak key generation
│ │ │
│ │ └─ Letters appear scrambled (right freq, wrong order)?
│ │ └─ Transposition
│ │ ├─ Rail fence → brute force rail count
│ │ └─ Columnar → try common key lengths
│ │
│ ├─ Numbers (digit pairs)?
│ │ ├─ Pairs in range 11-55 → Polybius square
│ │ └─ Numbers mod 26 → numeric substitution
│ │
│ ├─ Mixed case with pattern?
│ │ └─ Upper/lower encodes binary → Bacon cipher
│ │
│ └─ Non-printable bytes?
│ └─ XOR cipher
│ ├─ Single-byte key → brute force 256
│ ├─ Repeating key → xortool / Hamming distance
│ └─ Known plaintext → direct key recovery
│
└─ Step 3: Apply specific attack
├─ Substitution → quipqiup.com / frequency analysis
├─ Caesar → dcode.fr / brute force
├─ Vigenere → Kasiski + per-column Caesar
├─ Affine → brute force 312 combinations
├─ Hill → known-plaintext matrix attack
├─ Transposition → pattern analysis + brute force
└─ XOR → xortool / crib dragging未知密文 — 如何识别和破解?
│
├─ 步骤1:检查编码
│ ├─ 带填充的Base64字符集? → 先解码,再重新分析
│ ├─ 十六进制字符串? → 转换为字节,重新分析
│ ├─ 二进制(01)? → 转换为ASCII
│ ├─ 摩尔斯电码(.-/)? → 摩尔斯解码
│ └─ 可打印文本? → 继续步骤2
│
├─ 步骤2:字符集分析
│ ├─ 仅字母(A-Z)?
│ │ ├─ 计算IC
│ │ │ ├─ IC ≈ 0.065 → 单字母替换
│ │ │ │ ├─ 频率统一偏移? → Caesar → 暴力尝试25种偏移
│ │ │ │ ├─ 映射看起来随机? → 简单替换 → 频率分析
│ │ │ │ └─ 双字母组模式? → Playfair → 双字母组分析
│ │ │ │
│ │ │ ├─ IC ≈ 0.04-0.05 → 多字母替换
│ │ │ │ ├─ Kasiski检验 → 计算密钥长度
│ │ │ │ └─ 按位置频率分析 → 破解Vigenere
│ │ │ │
│ │ │ └─ IC ≈ 0.038 → 超长密钥或者一次性密码本
│ │ │ └─ 查找密钥复用或者弱密钥生成逻辑
│ │ │
│ │ └─ 字母看起来被打乱(频率正确,顺序错误)?
│ │ └─ 换位密码
│ │ ├─ 栅栏密码 → 暴力尝试栏杆数量
│ │ └─ 列换位 → 尝试常见密钥长度
│ │
│ ├─ 数字(数字对)?
│ │ ├─ 数字对范围11-55 → Polybius方格
│ │ └─ 数字模26 → 数字替换密码
│ │
│ ├─ 大小写混合有规律?
│ │ └─ 大小写编码二进制 → Bacon密码
│ │
│ └─ 非打印字节?
│ └─ XOR密码
│ ├─ 单字节密钥 → 暴力尝试256种可能
│ ├─ 重复密钥 → xortool / 汉明距离
│ └─ 已知明文 → 直接恢复密钥
│
└─ 步骤3:应用特定攻击
├─ 替换密码 → quipqiup.com / 频率分析
├─ Caesar → dcode.fr / 暴力破解
├─ Vigenere → Kasiski检验 + 按列Caesar破解
├─ 仿射密码 → 暴力尝试312种组合
├─ Hill → 已知明文矩阵攻击
├─ 换位密码 → 模式分析 + 暴力破解
└─ XOR → xortool / 拖库攻击11. TOOLS
11. 工具
| Tool | Purpose | URL/Usage |
|---|---|---|
| CyberChef | Universal encoding/cipher Swiss army knife | gchq.github.io/CyberChef |
| dcode.fr | 200+ cipher solvers online | dcode.fr |
| quipqiup | Automated substitution cipher solver | quipqiup.com |
| xortool | XOR cipher analysis and cracking | |
| RsaCtfTool | RSA + some classical cipher support | GitHub |
| Ciphey | Automated cipher detection and decryption | |
| hashID | Identify hash types | |
| Python | Custom frequency analysis and scripting | All attacks above |
| 工具 | 用途 | 地址/用法 |
|---|---|---|
| CyberChef | 通用编码/密码瑞士军刀 | gchq.github.io/CyberChef |
| dcode.fr | 200+种密码在线破解工具 | dcode.fr |
| quipqiup | 自动替换密码破解工具 | quipqiup.com |
| xortool | XOR密码分析与破解 | |
| RsaCtfTool | RSA + 部分古典密码支持 | GitHub |
| Ciphey | 自动密码检测与解密 | |
| hashID | 哈希类型识别 | |
| Python | 自定义频率分析和脚本开发 | 上述所有攻击 |
CyberChef Recipes (Common)
CyberChef常用配方
ROT13: ROT13
Caesar brute force: ROT13 (with offset slider)
Base64 decode: From Base64
Hex decode: From Hex
XOR: XOR (key as hex/utf8)
Vigenere: Vigenère Decode
Morse: From Morse CodeROT13: ROT13
Caesar暴力破解: ROT13 (使用偏移滑块)
Base64解码: From Base64
十六进制解码: From Hex
XOR: XOR (密钥为hex/utf8格式)
Vigenere: Vigenère Decode
摩尔斯: From Morse Code