转发注明出处: http://www.cnblogs.com/0zcl/p/6105825.html
一、基本概念
- 古典密码是基于字符替换的密码。加密技术有:Caesar(恺撒)密码、Vigenere(维吉尼尔)密码、Playfair密码、Hill密码……
- 明文:未经过加密的信息;密文:加密后的信息
- 加密解密的过程:如下图
二、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加密算法是先将明文按两个字母一组进行分组,然后在矩阵中找对应的密文。
取密文的规则如下:
- 若明文出现相同字母在一组,则在重复的明文字母中插入一个填充字母(eg:z)进行分隔后重新分组(eg: balloon被重新分组为ba lz lo on)
- 若分组到最后一组时只有一个字母,则补充字母z
- 若明文字母在矩阵中同行,则循环取其右边下一个字母为密文(矩阵最右边的下一个是最左边的第一个)(eg: ar被加密为RM)
- 若明文字母在矩阵中同列,则循环取其下边下一个字母为密文(矩阵最下边的下一个是最上边的第一个)(eg: mu被加密为CM)
- 若明文字母在矩阵中不同行不同列,则取其同行且与同组另一字母同列的字母为密文(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??
于是,测试如下:
通过测试,也没问题,因为解密时会把多余的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
源代码
测试用例