栅栏密码(Rail Fence Cipher)是一种很古老的换位密码——它不改字母本身,只改变字母的位置。"栏"数就是栅栏的行数,2栏和3栏的区别就在于把明文写在几行上。
CTF 和密码学教材里把栅栏密码区分为两种主流形式,也就是“标准型”和 “W型”(也叫波浪型)。虽然它们都叫“栅栏”,但加密方式完全不同。
1. 标准型(普通栅栏 / 分栏式)
这种在国内 CTF 中偶尔会出现,也叫“矩阵分栏”,或者”列置换密码”。
- 加密逻辑:把明文按顺序切成若干组(每组长度 = 栏数),然后竖着读。
- 举个例子:明文 123456789,栏数 3。
- 分组(每3个一组,因为栏数为3,则需要3行):123 | 456 | 789
- 排成矩阵: 1 2 3
4 5 6
7 8 9 - 按列读出:147 258 369 → 密文:147258369
- 解密:按长度切成 3 段,再逐列还原。
2. W型(真正的经典栅栏 / 波浪式)
这是国际上最公认的“Rail Fence Cipher”,也是你本题实际使用的类型(虽然只用了 2 栏)。
- 加密逻辑:把字母按 “Z”字形(或“W”折线) 写在 N 行上,最后按行横着读。
- 举个例子:明文 123456789,栏数 3。
- 走波浪轨迹(1→2→3→2→1→2→3…): 行1:1 5 9
行2: 2 4 6 8
行3: 3 7 - 按行拼接:159 + 2468 + 37 → 密文:159246837
- 走波浪轨迹(1→2→3→2→1→2→3…): 行1:1 5 9
🤔 对你这道“篱笆墙的影子”的影响
- 本题密文 felhaagv{…} ,栏数正好是 2。
- 在栏数为 2 时,标准型和 W 型的加密结果是一模一样的(因为只有两行,往上折返和直接分两段效果相同)。
⚠️ 易错提醒:如果以后碰到“栏数=3”或“栏数=4”的题,一定要先确认出题人用的是标准型还是 W 型,否则解出来是一堆乱码。通常题目若只写“栅栏密码”没说明,默认指 W 型;若写“分栏式”或给出明显分组痕迹,才是标准型。
W型的原理详细说明
把要加密的文字按 Z 字形(之字形,或者W型) 逐行往下写,写完再一行一行横着读出来,就是密文。解密则是反过来,先按长度把密文分到各行,再按 Z 字形走回去拼出原文。
🔢 2 栏W型举例
明文:HELLO WORLD(去掉空格方便看:HELLOWORLD,10 个字母)
加密过程——Z 字形写 2 行:
H . L . O . O . L . ← 第1栏(奇数位)
. E . L . W . R . D ← 第2栏(偶数位)
写法等价于:第 1、3、5… 个字母落第 1 栏,第 2、4、6… 个字母落第 2 栏。 两栏拼起来横读:
- 第 1 栏:HLOOL
- 第 2 栏:ELWRD 密文:HLOOLELWRD(有时写作 HLOOL ELWRD)
💡 2 栏的规律很简单:其实就是把原文奇偶位分开再拼接。解密时按密文长度一半(或接近)砍成两段,再交叉归位即可。
解密还原:
密文 HLOOLELWRD 长 10,前 5 个回第 1 栏,后 5 个回第 2 栏,交叉取 → H E L L O W O R L D
Python代码实现:
def encrypt_rail2(text: str) -> str:
"""2栏加密:奇偶分离"""
odd = text[::2] # 索引0,2,4…
even = text[1::2] # 索引1,3,5…
return odd + even
def decrypt_rail2(cipher: str) -> str:
"""2栏解密:交叉合并"""
n = len(cipher)
half = (n + 1) // 2 # 第一栏的长度(向上取整)
first = cipher[:half]
second = cipher[half:]
result = []
i = j = 0
# 交替取字符,注意原文字符总数可能奇数
while i < len(first) or j < len(second):
if i < len(first):
result.append(first[i]); i += 1
if j < len(second):
result.append(second[j]); j += 1
return ''.join(result)
# 测试
plain = "HELLOWORLD"
enc2 = encrypt_rail2(plain)
dec2 = decrypt_rail2(enc2)
print(f"2栏加密: {enc2}") # HLOOLELWRD
print(f"2栏解密: {dec2}") # HELLOWORLD
🔢 3 栏W型举例
明文还是 HELLOWORLD(10 个字母)
加密过程——Z 字形写 3 行:
H . . . O . . . R . ← 第1栏
. E . L . W . L . D ← 第2栏
. . L . . . O . . . ← 第3栏
按行横读拼起来:
- 第 1 栏:HOR
- 第 2 栏:ELWLD
- 第 3 栏:LO 密文:HORELWLDLO
解密稍微麻烦一点:要先算出每行分几个字母(按总长度和栏数推,周期 = 2×(栏数−1) = 4,10 个字符里第 1、4 栏会多拿一个),再把密文按算好的长度切回三行,最后按 Z 字形路径走回去。
Python代码实现:
def encrypt_rail3(text: str) -> str:
"""3栏加密:Z字形写入3行,逐行读出"""
rows = ['', '', '']
row = 0
direction = 1 # 1向下,-1向上
for ch in text:
rows[row] += ch
if row == 0:
direction = 1
elif row == 2:
direction = -1
row += direction
return rows[0] + rows[1] + rows[2]
def decrypt_rail3(cipher: str) -> str:
"""3栏解密:标记位置 → 填充 → 按Z字形读出"""
n = len(cipher)
# 用布尔矩阵记录哪些位置有字符
fence = [[False]*n for _ in range(3)]
row = 0
direction = 1
for col in range(n):
fence[row][col] = True
if row == 0:
direction = 1
elif row == 2:
direction = -1
row += direction
# 将密文字符按行顺序填入
idx = 0
for r in range(3):
for c in range(n):
if fence[r][c]:
fence[r][c] = cipher[idx]
idx += 1
# 按Z字形路径读出
result = []
row = 0
direction = 1
for col in range(n):
result.append(fence[row][col])
if row == 0:
direction = 1
elif row == 2:
direction = -1
row += direction
return ''.join(result)
# 测试
plain = "HELLOWORLD"
enc3 = encrypt_rail3(plain)
dec3 = decrypt_rail3(enc3)
print(f"3栏加密: {enc3}") # HORELWLDLO
print(f"3栏解密: {dec3}") # HELLOWORLD


