信息安全-1:python之playfair密碼算法詳解[原創]


轉發注明出處: http://www.cnblogs.com/0zcl/p/6105825.html 

 

一、基本概念

 

  1. 古典密碼是基於字符替換的密碼。加密技術有:Caesar(愷撒)密碼、Vigenere(維吉尼爾)密碼、Playfair密碼、Hill密碼……
  2. 明文:未經過加密的信息;密文:加密后的信息
  3. 加密解密的過程:如下圖

 

 

二、Caesar密碼

  這里我先簡單講下愷撒密碼,咸覺挺簡單的,后面再主要講Playfair算法原理與編程。

基本原理:

在開拓羅馬帝國的時候,愷撒擔心信使會閱讀他送給士兵的命令,因此發明了對命令進行加密的算法--愷撒密碼器

愷撒密碼器挺簡單的:把字母表中的每個字母向前循環移動3位

  • 加密信息將字母向前移動三位
  • 解密信息將字母向后移動三位
  • 移動的位數是關鍵,稱之為密鑰
  • 加密和解密的密鑰是相同的,我們稱之為對稱密碼器

 

數學表達

愷撒密碼的加密算法表示為:C=E(p)=(p+3) mod 26

愷撒密碼的解密算法表示為:p=D(C)=(C-3) mod 26

 

改進的愷撒密碼

明文的發送方和接收方事先協商好一個密鑰K(1<=K<=25),則:

愷撒密碼的加密算法表示為:C=E(p)=(p+K) mod 26

愷撒密碼的解密算法表示為:p=D(C)=(C-K) mod 26

 

 

三、Playfair密碼

基本原理

Playfair算法基於一個5*5的字母矩陣,該矩陣使用一個關鍵詞構造,方法是按從左到右、從上到下順序,填入關鍵詞的字母(去除重復字母)后,將字母表其作余字母填入。

例如: 關鍵詞取:monarchy時,字母矩陣為下圖如示(矩陣只能放25個字母,I與J同)

 

加密規則(重要)

Playfair加密算法是先將明文按兩個字母一組進行分組,然后在矩陣中找對應的密文。

取密文的規則如下:

  1. 若明文出現相同字母在一組,則在重復的明文字母中插入一個填充字母(eg:z)進行分隔后重新分組(eg: balloon被重新分組為ba lz lo on)
  2. 若分組到最后一組時只有一個字母,則補充字母z
  3. 若明文字母在矩陣中同行,則循環取其右邊下一個字母為密文(矩陣最右邊的下一個是最左邊的第一個)(eg: ar被加密為RM)
  4. 若明文字母在矩陣中同列,則循環取其下邊下一個字母為密文(矩陣最下邊的下一個是最上邊的第一個)(eg: mu被加密為CM)
  5. 若明文字母在矩陣中不同行不同列,則取其同行且與同組另一字母同列的字母為密文(eg: hs被加密為BP,ea被加密為IM或JM)

PS:上述規則第一次看時挺煩的,但照着例子看就一定可以看懂的!

舉例(這個例子后面測試會用到的):

明文為we are discovered save yourself,分組成為we ar ed is co ve re ds av ey ou rs el fz; 

用上述矩陣加密后的密文為:UG RM KC SX HM UF MK BT OX GC MV AT LU KV

 

 

 四、Playfair算法編程

這里我不想直接貼代碼。想仔細說下編程的思路以及中間遇到的一些問題。因為剛開始百度參考別人的博客都是直接貼代碼,沒說思路,看代碼又不大懂。搞得我都是自己編的代碼。

 

流程圖(重要)

我畫的流程圖網址https://www.processon.com/diagraming/5839ae8de4b086d1e7cba620

前程明亮

媽呀!流程圖已經把我想說的大都說了~~尷尬~

如果加密做了,解密真的很簡單的。接下來說下我遇到的幾個問題,卡住挺久的~

 

 

 問題及分析

 問題1. 一組字母是重復明文字母,則插入字母Z(我設的,你也可以設別的),如何實現??

我剛開始的做法是循環明文列表,依次找出第奇數(為什么不先找偶數?先找偶數之后就得去找下一個元素(奇數),怕下標越界)個元素,然后比較奇數前面一個元素(偶數)是否相同,相同則插入字母Z。然而問題來了?每次我插入一個Z,列表及下標就會變化,這樣就得重新循環插入才行!

1     for i in range(len(list_clear_text)):
2         if(i % 2 == 1):   #列表中的第d奇數個
3             if(list_clear_text[i] == list_clear_text[i-1]):   #第奇數個與前一個(偶數)是否相同
4                 list_clear_text.insert(i, "Z")   #有重復明文字母則插入一個填充字母Z 並且退出循環
5                 break

然后我們用個while循環調用上面代碼就可以了。接下來的問題是:如何退出while循環,即如何判斷列表沒有一組字母是重復的明文字母。這里我想了想,決定用一個count來計數,每一組明文字母不相同時,則count+1,當 count == int(len(list_clear_text) / 2)時便可退出循環!

 1 #處理一組字線是重復明文字母
 2 def deal_repeat(list_clear_text):
 3     count = 0    #計算列表中有多少組是不同的
 4     flag = False
 5     for i in range(len(list_clear_text)):
 6         if(i % 2 == 1):   #列表中的第d奇數個
 7             if(list_clear_text[i] == list_clear_text[i-1]):   #第奇數個與前一個(偶數)是否相同
 8                 list_clear_text.insert(i, "Z")   #有重復明文字母則插入一個填充字母Z 並且退出循環
 9                 break
10             if (list_clear_text[i] != list_clear_text[i - 1]):
11                 count += 1
12                 if count == int(len(list_clear_text) / 2):
13                     flag = True
14 
15     return list_clear_text,flag   #返回的是元組 (list_clear_text, flag)

啪啪啪!問題解決!接下來說個解密時遇到的問題。

 

問題2:解密時先判斷列表最后一個是否Z,是的話刪除,接着問題來了,如何刪除加密時在一組重復的明文中填充的字母Z???

我剛開始的做法是先判斷(decryption_list[i]為第偶數個,若與decryption_list[i+2]相同,並且中間decryption_list[i+1] == "Z"等於Z,則刪除中間的Z)

1     for i in range(len(decryption_list)):
2         if i % 2 == 0:
3             if i+2 < len(decryption_list) and \
4                             decryption_list[i] == decryption_list[i+2] and decryption_list[i+1] == "Z":
5                 delete_list.pop(i+1)

為了方便看上面的判斷,我貼下測試:

請輸入明文:aabbbcccc
['a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c']
['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'Z']      #最后添加字母Z

問題來了,和問題1一樣,每刪除一個Z,列表與下標也隨之變化了啊!怎么辦??像問題1那樣循環刪除?這樣搞,我有幾條命都不夠用來打代碼。有沒有簡單的?啪啪啪。靈光一現,我可以從后往前刪除,這樣就不怕列表下標變化了,用一個空列表來裝我要刪除元素的下標。

 1     delete_list = []
 2     for i in range(len(decryption_list)):
 3         if i % 2 == 0:          #第偶數個
 4             #不越界
 5             if i+2 < len(decryption_list) and \
 6                             decryption_list[i] == decryption_list[i+2] and decryption_list[i+1] == "Z":
 7                 delete_list.append(i+1)
 8                 #decryption_list.pop(i+1)
 9     delete_list.reverse()     #反序,從后往前刪除,每次刪完下標就不會再變化,我真是太聰明了!
10     for i in delete_list:
11         print(i)
12         decryption_list.pop(i)

 

 

問題3:我設定的是插入Z,當時我想的是如果一組重復的明文剛好是zz,那還能插入Z(我設定的)?以及如果明文列表最后一個剛好是z,那我還能在最后補充Z??

於是,測試如下:

 1 Please input E for encryption or D for decryption:E
 2 請輸入明文:aabbbcccczz
 3 ['a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'z', 'z']
 4 ['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'z', 'z']
 5 ['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'z', 'z', 'Z']
 6 加密成功!密文:RXBJDXDHDUDUDUUU
 7 Please input E for encryption or D for decryption:D
 8 請輸入密文(大寫字母/偶數):RXBJDXDHDUDUDUUU
 9 11
10 9
11 5
12 1
13 解密成功!明文:AABBBCCCCZZ
14 Please input E for encryption or D for decryption:E
15 請輸入明文:aazzbbbcccczz
16 ['a', 'a', 'z', 'z', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'z', 'z']
17 ['a', 'Z', 'a', 'z', 'z', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'z', 'z']
18 ['a', 'Z', 'a', 'z', 'z', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'z', 'z', 'Z']
19 加密成功!密文:RXRXXDDXDHDUDUDUUU
20 Please input E for encryption or D for decryption:D
21 請輸入密文(大寫字母/偶數):RXRXXDDXDHDUDUDUUU
22 13
23 11
24 7
25 1
26 解密成功!明文:AAZZBBBCCCCZZ
27 Please input E for encryption or D for decryption:
View Code

通過測試,也沒問題,因為解密時會把多余的Z刪除。(看來我想多了~)

 

 

問題4: 好尷尬的BUG

加密時,不論時文abc, 還是abcz,加密后的密文均為BJDU,那慘了,那BJDU解密時,是abc?還是abcz??

Please input E for encryption or D for decryption:E 請輸入明文(小寫字母):ABC ['A', 'B', 'C'] ['A', 'B', 'C'] ['A', 'B', 'C', 'Z'] 加密成功!密文:BJDU Please input E for encryption or D for decryption:D 請輸入密文(大寫字母/偶數):BJDU 解密成功!明文:ABC Please input E for encryption or D for decryption:E 請輸入明文(小寫字母):abcz ['a', 'b', 'c', 'z'] ['a', 'b', 'c', 'z'] ['a', 'b', 'c', 'z'] 加密成功!密文:BJDU Please input E for encryption or D for decryption:D 請輸入密文(大寫字母/偶數):BJDU 解密成功!明文:ABC Please input E for encryption or D for decryption:

不管了,大家不必死鑽這個BUG,如果有啥好辦法可以與我交流下,我吃飯去了……

 

 五、總結

 

  • 有半個月沒用python了,列表操作有些竟然忘了。尷尬~
  • list.pop()刪除指定下標的元素,默認刪除最后一個元素
  • 列表的list.reverse()返回值是None
  • 方法return 1,2  其實是返回一個元組(1, 2)
  • print(int(7/2)) 輸出3

 

 

 源代碼

 

  1 # 5*5的矩陣, I與J同, 注意是大寫的,也可以自己定義矩陣(不要重復)
  2 SECRET_LIST = [["M","O","N","A","R"],
  3                ["C","H","Y","B","D"],
  4                ["E","F","G","J","K"],
  5                ["L","P","Q","S","T"],
  6                ["U","V","W","X","Z"]]
  7 
  8 #加密
  9 def encryption(clear_text):
 10     #去除空格與逗號
 11     clear_text = clear_text.replace(" ", "").replace(",", "")
 12     list_clear_text = list(clear_text)   #字符串轉化為列表
 13     print(list_clear_text)
 14     #兩個字母為一組,在重復的明文字母中插入填充字母Z
 15     flag = False
 16     while not flag:
 17         list_clear_text = deal_repeat(list_clear_text)[0]
 18         flag = deal_repeat(list_clear_text)[1]
 19     print(list_clear_text)
 20     #若分組到最后一組時只有一個字母,則補充字母Z
 21     if len(list_clear_text) % 2 == 1:
 22         list_clear_text.append("Z")
 23     print(list_clear_text)
 24     #加密的核心代碼
 25     encryption_list = core_encryption(list_clear_text)  #返回密文列表
 26     return ("").join(encryption_list)
 27 
 28 
 29 #處理一組字線是重復明文字母
 30 def deal_repeat(list_clear_text):
 31     count = 0    #計算列表中有多少組是不同的
 32     flag = False
 33     for i in range(len(list_clear_text)):
 34         if(i % 2 == 1):   #列表中的第d奇數個
 35             if(list_clear_text[i] == list_clear_text[i-1]):   #第奇數個與前一個(偶數)是否相同
 36                 list_clear_text.insert(i, "Z")   #有重復明文字母則插入一個填充字母Z 並且退出循環
 37                 break
 38             if (list_clear_text[i] != list_clear_text[i - 1]):
 39                 count += 1
 40                 if count == int(len(list_clear_text) / 2):
 41                     flag = True
 42 
 43     return list_clear_text,flag   #返回的是元組 (list_clear_text, flag)
 44 
 45 #獲得字母在矩陣中的行與列
 46 def get_rows_columns(alphabet):
 47     if alphabet == "I":  #矩陣中只有25個字母,I同J
 48         alphabet = "J"
 49     for i in range(len(SECRET_LIST)):
 50         for j in range(len(SECRET_LIST[i])):
 51             if (SECRET_LIST[i][j] == alphabet):
 52                 return i,j
 53 
 54 
 55 # 加密的核心代碼,先找出每一組字母在5*5矩陣 的行與列
 56 def core_encryption(list_clear_text):
 57     encryption_list = []
 58     for i in range(len(list_clear_text)):
 59         if(i % 2 == 0):
 60             x = list_clear_text[i].upper()   #將一組字母轉為大寫,因為矩陣的字母全是大寫的
 61             y = list_clear_text[i+1].upper()
 62             x_tuple = get_rows_columns(x)   #返回元組形式
 63             y_tuple = get_rows_columns(y)
 64             # print(x_tuple)
 65             # print(y_tuple)
 66             if x_tuple[0] == y_tuple[0]:   #若明文字母在矩陣中同行
 67                 x_secret = SECRET_LIST[x_tuple[0]][(x_tuple[1] + 1) % 5]
 68                 y_secret = SECRET_LIST[y_tuple[0]][(y_tuple[1] + 1) % 5]
 69             elif x_tuple[1] == y_tuple[1]:  #若明文字母在矩陣中同列
 70                 x_secret = SECRET_LIST[(x_tuple[0] + 1) % 5][x_tuple[1]]
 71                 y_secret = SECRET_LIST[(y_tuple[0] + 1) % 5][y_tuple[1]]
 72             else:      #若明文字母在矩陣中不同行不同列
 73                 x_secret = SECRET_LIST[x_tuple[0]][y_tuple[1]]
 74                 y_secret = SECRET_LIST[y_tuple[0]][x_tuple[1]]
 75             encryption_list.append(x_secret)
 76             encryption_list.append(y_secret)
 77     return encryption_list      #返回字母加密后的列表
 78 
 79 
 80 #解密核心代碼,返回解密后的明文列表,密文肯定是偶數的,每一組密文字母也肯定是不同的
 81 def core_decryption(list_cipher_text):
 82     decryption_list = []
 83     for i in range(len(list_cipher_text)):
 84         if(i % 2 == 0):
 85             x = list_cipher_text[i]
 86             y = list_cipher_text[i+1]
 87             x_tuple = get_rows_columns(x)   #返回元組形式
 88             y_tuple = get_rows_columns(y)
 89             if x_tuple[0] == y_tuple[0]:   #若密文字母在矩陣中同行
 90                 x_clear = SECRET_LIST[x_tuple[0]][(x_tuple[1] - 1) % 5]
 91                 y_clear = SECRET_LIST[y_tuple[0]][(y_tuple[1] - 1) % 5]
 92             elif x_tuple[1] == y_tuple[1]:  #若密文字母在矩陣中同列
 93                 x_clear = SECRET_LIST[(x_tuple[0] - 1) % 5][x_tuple[1]]
 94                 y_clear = SECRET_LIST[(y_tuple[0] - 1) % 5][y_tuple[1]]
 95             else:      #若密文字母在矩陣中不同行不同列
 96                 x_clear = SECRET_LIST[x_tuple[0]][y_tuple[1]]
 97                 y_clear = SECRET_LIST[y_tuple[0]][x_tuple[1]]
 98             decryption_list.append(x_clear)
 99             decryption_list.append(y_clear)
100     return decryption_list       #返回解密后的明文列表(需進一步處理,eg:去掉Z)
101 
102 #解密
103 def decryption(cipher_text):
104     cipher_text = cipher_text.replace(" ", "").replace(",", "")
105     list_cipher_text = list(cipher_text.strip())   #將密文轉化為列表
106     decryption_list = core_decryption(list_cipher_text)   #調用函數
107     if decryption_list[-1] == "Z":      #若列表最后一個元素是Z,則刪除
108         decryption_list.pop(-1)
109     #找出列表應該刪除的下標
110     delete_list = []
111     for i in range(len(decryption_list)):
112         if i % 2 == 0:          #第偶數個
113             #不越界
114             if i+2 < len(decryption_list) and \
115                             decryption_list[i] == decryption_list[i+2] and decryption_list[i+1] == "Z":
116                 delete_list.append(i+1)
117                 #decryption_list.pop(i+1)
118     delete_list.reverse()     #反序,從后往前刪除,每次刪完下標就不會再變化,我真是太聰明了!
119     for i in delete_list:
120         print(i)
121         decryption_list.pop(i)
122     return "".join(decryption_list)
123 
124 
125 
126 if __name__ == "__main__":
127     while True:
128         choice = input("Please input E for encryption or D for decryption:")
129         if choice.strip() != "E" and choice.strip() != "D":
130             print("Input Error")
131         #加密
132         if choice.strip() == "E":
133             clear_text = input("請輸入明文:")
134             print("加密成功!密文:%s" % encryption(clear_text))
135         #解密
136         if choice.strip() == "D":
137             cipher_text = input("請輸入密文(大寫字母/偶數):")
138             print("解密成功!明文:%s" % decryption(cipher_text))
View Code

 

 測試用例

 1 Please input E for encryption or D for decryption:E
 2 請輸入明文:we are discovered save yourself
 3 ['w', 'e', 'a', 'r', 'e', 'd', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'e', 'd', 's', 'a', 'v', 'e', 'y', 'o', 'u', 'r', 's', 'e', 'l', 'f']
 4 ['w', 'e', 'a', 'r', 'e', 'd', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'e', 'd', 's', 'a', 'v', 'e', 'y', 'o', 'u', 'r', 's', 'e', 'l', 'f']
 5 ['w', 'e', 'a', 'r', 'e', 'd', 'i', 's', 'c', 'o', 'v', 'e', 'r', 'e', 'd', 's', 'a', 'v', 'e', 'y', 'o', 'u', 'r', 's', 'e', 'l', 'f', 'Z']
 6 加密成功!密文:UGRMKCSXHMUFMKBTOXGCMVATLUKV
 7 Please input E for encryption or D for decryption:D
 8 請輸入密文(大寫字母/偶數):UGRMKCSXHMUFMKBTOXGCMVATLUKV
 9 解密成功!明文:WEAREDJSCOVEREDSAVEYOURSELF
10 Please input E for encryption or D for decryption:E
11 請輸入明文:aabbbcccc
12 ['a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
13 ['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c']
14 ['a', 'Z', 'a', 'b', 'b', 'Z', 'b', 'c', 'c', 'Z', 'c', 'Z', 'c', 'Z']
15 加密成功!密文:RXBJDXDHDUDUDU
16 Please input E for encryption or D for decryption:D
17 請輸入密文(大寫字母/偶數):RXBJDXDHDUDUDU
18 11
19 9
20 5
21 1
22 解密成功!明文:AABBBCCCC
23 Please input E for encryption or D for decryption:
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM