1. 原理介紹
柵欄密碼按照排列的順序將明文(去掉空格)寫入 m 行 n 列的數組,按照行的順序將字符重新組合得到密文,這種方法稱為 m 欄柵欄密碼。比較常見的是 2 欄柵欄密碼。一般來說,是給定 m,然后通過加密文本的長度確定 n。以文本 ILoveYou,YouLoveMe,行數 m=4 為例:
1.1 加密
(1) 首先將文本按如下方式排列,按列將文本排列成 m 行的柵欄
I | e | , | L | M |
---|---|---|---|---|
L | Y | Y | o | e |
o | o | o | v | |
v | u | u | e |
(2) 將文本按行取出,得到密文 Ie,LMLYYoeooovvuue
事實上,加密過程就是把明文按 m 個分組,先取每組第 1 個,再取每組第 2 個,空缺的部分跳過,直至全部取完為止
1.2 解密
(1) 首先通過密文的長度 len = 18 和柵欄的欄數 m = 4 確定柵欄的列數 n;
計算 len / m 得到商 q 和余數 r,如果余數 r = 0,那么 n = q;否則 n = q + 1;
此處 n = 5
(2) 將密文按行排列,重新生成 m 行 n 列的數組
I | e | , | L | M |
---|---|---|---|---|
L | Y | Y | o | e |
o | o | o | v | |
v | u | u | e |
(3) 將上述數組由上至下,由左至右讀取,恢復明文 ILoveYou,YouLoveMe
事實上,解密過程就是先計算出 n 和 r 后,將密文分成 m 組,前 m - r 組按 n 為單位分組,后 r 組按 n - 1 為單位分組,按照類似加密的方式執行,還原出明文
(1) 計算出 n = 5,r = 2,mid = (m - r) · n = 10, Len = 18
(2) 前 mid 個密文以 5 為單位分組,后 Len - mid = 8 個密文以 n - 1 = 4 為單位分組,得到
I | L | o | v |
---|---|---|---|
e | Y | o | u |
, | Y | o | u |
L | o | v | e |
M | e |
(3) 按照加密的方式讀取分組,得到明文
2. 具體實現(python)
2.1 加密
def encrypt(plaintext: str, m: int):
"""
:param plaintext: 明文
:param m: 欄數
:return: 密文
"""
ciphertext, length = [], len(plaintext)
for i in range(m): # 取出每個分組第 i 個元素
for j in range(i, length, m):
ciphertext.append(plaintext[j])
return ''.join(ciphertext)
2.2 解密
def decrypt(ciphertext: str, m: int):
"""
:param ciphertext: 密文
:param m: 欄數
:return: 明文
"""
plaintext, length = [], len(ciphertext)
q, r = divmod(length, m)
n = (q + 1) if r else q # 計算 n
mid = n * (m - r)
for i in range(n - 1):
# 前 m-r 個分組
for j in range(i, mid, n):
plaintext.append(ciphertext[j])
# 后 r 個分組
for j in range(mid + i, length, n - 1):
plaintext.append(ciphertext[j])
# 前 m-r 個分組最后一個元素
for j in range(n - 1, mid, n):
plaintext.append(ciphertext[j])
return ''.join(plaintext)
2.3 測試樣例及結果
# 樣例一
plaintext = "whateverisworthdoingisworthdoingwell"
m = 3 # 柵欄行數
ciphertext = "wtesrdnsrdneherwtogwtoglaviohiiohiwl"
# 樣例二
plaintext = "healthismoreimportantthanwealth"
m = 2
ciphertext = "hatimriprathnelhelhsoemotntawat"
3. 其它
柵欄密碼除了上述的方式,還有一種為 W型柵欄,此處不作介紹
參考資料:《密碼學實驗教程》