用一首著名的英文詩,手把手演示如何破解簡單替換加密。
百家號不支持代碼格式,文章裡的代碼排版都是亂的。
如果需要拷貝代碼,可以去同名微信公眾號。
上篇說到,李雷用凱撒加密,仿射加密給Kate寫信,都被父母輕鬆破解了。
李雷只好放大招,使用了簡單替換加密。
因為簡單替換加密有403291461126605635584000000個不重複的秘鑰!即使電腦一秒鐘可以嘗試1000萬次,暴力破解也需要120萬年。
在信裡,李雷抄了一首著名的英文詩,加密之後如下。
有興趣的話,可以先不看後面的內容,試著去破解它。
Tgm fiztgmqt usqtycam sc tgm klzru
Sq clt vmtkmmc rsfm ycu umytg
Vit kgmc S qtycu sc fzlct lf xli
Xmt xli ulc't wclk tgyt S rlom xli.
Tgm fiztgmqt usqtycam sc tgm klzru
Sq clt kgmc S qtycu sc fzlct lf xli
Xmt xli ayc't qmm nx rlom
Vit kgmc iculivtrx wclkscp tgm rlom fzln vltg
Xmt aycclt vm tlpmtgmz.
Tgm fiztgmqt usqtycam sc tgm klzru
Sq clt vmscp yeyzt kgsrm vmscp sc rlom
Vit kgmc S eryscrx aycclt zmqsqt tgm xmyzcscp
Xmt ezmtmcuscp xli gyom cmomz vmmc sc nx gmyzt.
Tgm fiztgmqt usqtycam sc tgm klzru
Sq clt qtzipprscp ypyscqt tgm tsumq
Vit iqscp lcm'q scusffmzmct gmyzt
Tl usp yc icazlqqyvrm zsomz
Flz tgm lcm kgl rlomq xli
Python實現簡單替換加密?
簡單替換加密的原理很簡單。
就是給26個英文字母都挑選一個替換的字母。
字母A,可以從A~Z,26個字母裡任意挑一個,有26種可能。
假設挑選了字母 X 來替換 A。
字母B,不能再挑選X,只能從剩下的25個字母裡挑選,所以有25種可能。
……
以此類推,一共有
26*25*24*……*1 = 403291461126605635584000000種可能。
假設我們這次挑選的字母替換關係如下
我們用它來加密愛爾蘭著名詩人葉芝的詩《當你老了》中的第一句
When you are old and grey and full of sleep
message = 'When you are old and grey and full of sleep'
source = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
key = 'XRMAFGZBWDCEHOIVLKTJNYSQPU'
entry = ''
for i in range(len(message)):
if message[i].isalpha():
if message[i].islower():
entry += key[source.find(message[i].upper())].lower()
else:
entry += key[source.find(message[i])]
else:
entry += message[i]
print(entry)
上面的程序,就是逐一遍歷這句詩的每一個字母。
通過find函數找到字母在正常字母表source裡的位置,
然後把它替換成密文字母表key裡位置相同的字母。
可見,簡單替換加密的實現很簡單。
但是破解缺需要一些技巧。
第一招:字母頻率
在例子中,詩句的原文
When you are old and grey and full of sleep
出現得最多的字母是 e,一共出現了6次,其次是字母 l,出現了4次。
而加密之後的密文
Sbfo pin xkf iea xoa zkfp xoa gnee ig teffv
出現的最多的字母是f,也出現了6次。
道理很簡單,因為原文中所有的e都替換成了f。
所以,我們有了一個大膽的猜想,如果英文裡,每個字母出現的頻率是固定的。
比如說在所有的英文裡,字母e是出現頻率最高的,那麼是不是只要找出密文裡出現次數最多的那個字母,它對應的明文一定就是字母e呢?
這就是破解簡單替換加密的第一招。
如下圖,密碼學家通過對海量的英文文章進行統計,發現26個英文字母出現的頻率順序如下。
ETAONRISHDLFCMUGYPWBVKJXQZ
不過,字母頻率只能用來做參考。
比如上的密文
Sbfo pin xkf iea xoa zkfp xoa gnee ig teffv
f出現了6次,頻率最高,對應 e,是正確的。
但是密文裡出現次數第2多的是 e,如果按照字母頻率的順序,它應該對應字母 t,但實際上,它對應的明文是 l 。
很顯然,文章越長,字母頻率越準確,文章越短,不確定性就越大。
當然,也有例外。
比如在法文裡,出現得最多的字母也是e,但是法國著名作家George Perec就寫了一本小說《La Disparition》(消失),裡面一個字母e都沒有。
試著用第一招來破解
不妨試一試用字母頻率來進行破解。
先把密文保存成文件 mail.txt
接下來就是統計密文裡每個字母出現的頻率
count = {}
with open('data/mail.txt') as fr:
lines = fr.readlines()
for line in lines:
for i in range(len(line)):
if line[i].isalpha():
if line[i].upper() in count:
count[line[i].upper()] += 1
else:
count[line[i].upper()] = 1
count = sorted(count.items(),key=lambda item:item[1],reverse=True)
result = ''
for i in count:
result += i[0]
print(result)
執行程序,得到密文裡字母的頻率排序是
MTCLSGZQYIURXFKVPAONEW
可以試著把密文裡的每個字母按頻率進行替換。
ETAONRISHDLFCMUGYPWBVKJXQZ
也就是說密文裡的字母 M替換成 字母 E,密文裡的字母 T 替換成字母 T ……
但是,最後破解出來的結果如下,顯然不正確。
第二招:單詞模式
先來看例詩中的一個單詞 sleep
如果我們用下面的方法來表示這個單詞
第一個出現的字母 s,記為 0
第2個字母 l,和第一個字母不同,記為 1
第3個字母e,和前2個字母不同,記為 2
第4個字母 e,和第3個字母相同,所以還是記為 2
第5個字母 p,和前面的字母都不同,記為 3
所以,sleep 記為 01223
在我們的例子裡,sleep經過加密之後,密文是 teffv
如果用同樣的方法,teffv 也記為 01223。
我們稱這種方法為單詞模式
很顯然,經過簡單替換加密之後,單詞模式並不會發生改變。
如果我們知道每個英文單詞的單詞模式,那麼,是不是就可以通過單詞模式進行破解呢?
這就是破解簡單替換加密的第二招:單詞模式。
當然,首先你得有所有的英文單詞。
網上並沒有現成的單詞庫,不過好在我們可以通過爬蟲去爬取(爬蟲的知識後面再介紹),如下,已經爬取好了16335個單詞,並保存成了 dic.txt文件。
其次,還得把每個單詞轉成單詞模式。
程序如下,其中 函數 calculate就是專門用來將單詞轉換成單詞模式的。
def calculate(message):
dicTmp = {}
k = 0
result = ''
for i in range(len(message)):
if message[i] not in dicTmp:
dicTmp[message[i]] = k
k += 1
result += str(dicTmp[message[i]])
else:
result += str(dicTmp[message[i]])
return result
dic = {}
with open('data/dic.txt') as fr:
lines = fr.readlines()
for line in lines:
word = line.strip()
rs = calculate(word)
if rs in dic :
dic[rs] = dic[rs]+','+word
else:
dic[rs] = word
print(dic)
轉換之後的效果如下。
我們看到,單詞模式 0 ,只有單詞 a 和單詞 i 。想一想,一個字母就是一個單詞的是不是就只有這2個字母。
再比如,單詞模式是 0102342的只有一個單詞 abandon
當然,我們也看到,有的單詞模式有很多個單詞,比如 01023
試著用第二招來破解
接下來,我們把密文文件 mail.txt 裡的每一個單詞轉換成單詞模式。
然後從單詞庫裡去找相同單詞模式的單詞。
當然,我們先找那些一個單詞模式只有少量單詞的,比如先限制小於10個單詞。
with open('data/mail.txt') as fr:
lines = fr.readlines()
for line in lines:
line = line.replace(',','').replace('.','').replace(';','').strip()
words = line.split(' ')
for i in range(len(words)):
re = calculate(words[i])
if re in dic and 0<= dic[re].count(',')<10:
print(re,words[i],dic[re])
執行程序,我們發現,有2個密文單詞,找到了唯一匹配的明文單詞。
分別是
ezmtmcuscp ,單詞模式 0123245647,匹配的明文單詞只有quarantine
scusffmzmct,單詞模式01203345416 ,匹配的明文單詞只有indifferent
根據這2個單詞,就可以開始破解了。
先製作這樣一個表格
根據第一個單詞 ezmtmcuscp對應quarantine,可以填上
再根據第二個單詞scusffmzmct,對應indifferent,可以繼續補充
但是,發現這2個單詞破解出來的字母映射關係不一致。應該怎麼處理呢?
這時,第一招字母頻率就可以發揮作用了。
密文中,字母出現次數排序是MTCLSGZQYIURXFKVPAONEW
而英文中字母出現次數排序是 ETAONRISHDLFCMUGYPWBVKJXQZ
從頻率上分析,m對應的明文字母應該是e,t對應的應該是t,顯然第二個單詞更可信。所以,我們只保留第二個單詞的字母映射。
接下來,繼續看第三個密文單詞tlpmtgmz 它的單詞模式是01230435,可能的單詞有三個sadistic,sidestep,together。
但是根據第二個單詞已經整理出來的映射關係,t-->t,只有單詞together滿足要求。同時根據 tlpmtgmz --> together,又可以多得到 l --> o,p --> g,g--> h
以此類推,可以破解出
有了這麼多字母的映射關係,其實可以試著去破解整首詩了。
密文的第一句是
Tgm fiztgmqt usqtycam sc tgm klzru
用已知的字母映射破解得到
The furthest distance in the wor?d
雖然,密文裡字母r對應什麼字母還不知道,但根據整個句子的意思,很容易就能猜到,完整的句子應該是
The furthest distance in the world
這首詩就是著名的《世界上最遙遠的距離》。
關於這首詩的作者,一說出自張小嫻的小說《荷包裡的單人床》,另一說出自印度著名詩人泰戈爾詩集《飛鳥集》。
黔驢技窮的李雷
連理論上無法破解的簡單替換加密都被破解了,你還能為李雷想到什麼更安全的加密方式嗎?