python 密码学编程 -- 2
接上一篇随笔
********************************************************************
* quote : "http://inventwithpython.com/" *
* python-version : 2.7.11 *
********************************************************************
1.通过编程来检测英文
计算机无法理解英文。但是和乱码相比,英文是由可以在字典里找到的单词组成的。
我们可以通过判断一个单词是不是存在于某个字典来判断这个单词是不是英文。
当然,可能我们的字典不够大,导致某些正确的英文单词被判断成乱码。或者乱码碰巧刚好是一个英文单词。
但是我们说。在字典足够大且单词多为常用词的情况下,发生这些事的概率是比较小的。
我们说,当一个字符串中的大多数参数都被包含在这个英文字典里,我们就说这是一个英文字符串。
这里提供一本英文字典:http://invpy.com/dictionary.txt
要运行下面这个程序,你需要下载这个字典并且和 py 文件放在同一个目录下
detectEnglish.py
#-*-coding:utf-8-*-
UPPERLETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n' def loadDictionary(): #返回一个dict。其中包含了dictionary.txt 中的所有单词
dictionaryFile = open("dictionary.txt")
englishWords = {}
for word in dictionaryFile.read().split('\n'):
englishWords[word] = None
dictionaryFile.close()
return englishWords ENGLISH_WORDS = loadDictionary() def removeNonLetters(message): #只保留原字符串中的英文和空格部分
lettersOnly = []
for symbol in message:
if symbol in LETTERS_AND_SPACE:
lettersOnly.append(symbol)
return ''.join(lettersOnly) def getEnglishcount(message): #返回一个占比值。即这个message中的英文单词占(百分)比多少
message = message.upper()
message = removeNonLetters(message)
possibleWords = message.split() #预处理。得到原message中所有单词组成的列表 if possibleWords == []:
return 0.0 matches = 0
for word in possibleWords:
if word in ENGLISH_WORDS:
matches += 1
return float(matches) / len(possibleWords) def isEnglish(message , wordPercentage = 20, letterPercentage = 85):
wordsMatch = getEnglishcount(message) * 100 >= wordPercentage
numLetters = len(removeNonLetters(message))
messageLettersPercentage = float(numLetters) / len(message) *100
lettersMatch = messageLettersPercentage >= letterPercentage
return wordsMatch and lettersMatch
具体实现。我们把一个字符串删除数字和特殊符号之后分成一个一个单词的列表。
当这些单词中至少有20%是被包含在字典中。
并且原字符串至少85%是英文字母或者空格时(非数字和特殊字符)时。
我们就判断这个字符串是英文消息。
2.暴力破译换位加密法
从理论上来说,当要加密的消息为 message 时,换位加密法可能的秘钥有 len(message)-2 个(从 2 到 len(message)-1)
对于每一个用可能的秘钥破译出来的明文,我们需要判断这个明文是不是英文消息,从而判断破译是否成功。
于是。我们可以和上一个程序联系起来。让电脑自动判断破译出来的消息是否为英文。
transpositionHacker.py
#-*-coding:utf-8-*-
import detectEnglish , transpositionDecrypt def main():
myMessage = """Cb b rssti aieih rooaopbrtnsceee er es no npfgcwu plri ch nitaalr eiuengiteehb(e1 hilincegeoamn fubehgtarndcstudmd nM eu eacBoltaeteeoinebcdkyremdteghn.aa2r81a condari fmps" tad l t oisn sit u1rnd stara nvhn fsedbh ee,n e necrg6 8nmisv l nc muiftegiitm tutmg cm shSs9fcie ebintcaets h aihda cctrhe ele 1O7 aaoem waoaatdahretnhechaopnooeapece9etfncdbgsoeb uuteitgna.rteoh add e,D7c1Etnpneehtn beete" evecoal lsfmcrl iu1cifgo ai. sl1rchdnheev sh meBd ies e9t)nh,htcnoecplrrh ,ide hmtlme. pheaLem,toeinfgn t e9yce da' eN eMp a ffn Fc1o ge eohg dere.eec s nfap yox hla yon. lnrnsreaBoa t,e eitsw il ulpbdofgBRe bwlmprraio po droB wtinue r Pieno nc ayieeto'lulcih sfnc ownaSserbereiaSm-eaiah, nnrttgcC maciiritvledastinideI nn rms iehn tsigaBmuoetcetias rn"""
hackedMessage = hackTransposition(myMessage) if hackedMessage == None:
print 'Failed to hack encryption'
else:
print 'Copying hacked message to clipboard:'
print hackedMessage def hackTransposition(message):
print 'Hacking' for key in xrange(1 , len(message)):
print 'Trying key #%s ...'%(key)
decryptedText = transpositionDecrypt.decryptMessage(key , message)
if detectEnglish.isEnglish(decryptedText):
print ''
print 'Possible encryption hack : '
print 'Key %s : %s'%(key ,decryptedText[:100])
print ''
print 'If you think the message is Ok , Enter D for done , or just press Enter to continue hacking :'
resp = raw_input('>>>') if resp.strip().upper().startswith('D'):
return decryptedText
return None if __name__ == '__main__':
main()
这段代码的主要函数都在另外两个py文件里。具体的代码和功能我们在之前有叙述。这里不重复。
3.仿射加密法
我们先来说一下乘数加密法。
类似于凯撒加密法。凯撒加密时我们是加上了一个秘钥得到了一个对应关系。那现在如果我们乘上一个秘钥呢?
和凯撒加密法相比,乘数加密法的优势是可以使用很大的秘钥。它的对应关系的周期更大。
但是乘数加密法一个缺点是字母 A 对应的数字是 0 。它在乘上秘钥之后仍然是 0 。那么对任意秘钥,字母 A 就永远对应字母 A。
现在我们在进行乘数加密法之后,再进行凯撒加密法,就可以解决这个问题。
这意味着,我们需要两个秘钥,假设为 A 和 B
明文-->乘上秘钥 A -->加上秘钥 B -->根据符号集的大小取模-->密文
明文<--根据符号集的大小进行取模运算<--乘上秘钥 A 的模逆<--减去秘钥 B <--密文
这就是仿射加密法。
但是。乘数加密法的秘钥 A 存在一个问题。
例如当你选择秘钥 A = 8 时。字母 C 和 P 都被加密成了 Q。这样在解密的时候就无法判断原明文。
实际上。秘钥A 和符号集的大小必须互质。 也就是说,两者的最大公约数必须为1。
另外。如果你足够细心的话。会发现解密的时候我们并没有 乘以 A 的倒数,而是乘上 A 的模逆。
原因在此不详细谈论。
我们先写两个函数来帮助我们求最大公约数和模逆。
cryptomath.py
#cryptomath.py
#-*-coding:utf-8-*-
def gcd(a , b): #求最大公约数
while a != 0:
a ,b = b%a , a
return b def findModInverse(a , m): #欧几里得 扩展算法求模逆
if gcd(a , m) != 1:
return None
u1 , u2 , u3 = 1 , 0 , a
v1 , v2 , v3 = 0, 1, m
while v3 != 0:
q = u3 / v3
v1 , v2 , v3 , u1 , u2 , u3 = (u1 - q * v1) , (u2 - q * v2) , (u3 - q * v3) , v1 , v2 , v3
return u1 % m
算法详见百度。
affineCipher.py
#-*-coding:utf-8-*-
import sys , cryptomath , random
SYMBOLS = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""" def main():
myMessage = """"A computer would deserve to be called intelligent if it could deceive a human into believing that it was human." -Alan Turing"""
myKey = 2023
myMode = 'encrypt' if myMode == 'encrypt':
translated = encryptMessage(myKey , myMessage)
elif myMode == 'decrypt':
translated = decryptMessage(myKey , myMessage)
print 'Key : %s'%(myKey)
print '%sed text :'%(myMode.title())
print translated
print ''
translated = decryptMessage(myKey , translated)
print 'Decrypted test :'
print translated def getKeyParts(key): #由一个秘钥得到两部分秘钥
keyA = key/len(SYMBOLS)
keyB = key%len(SYMBOLS)
return (keyA , keyB) def checkKeys(keyA , keyB , mode):
if keyA == 1 and mode == 'encrypt': #keyA 为 1 时,秘钥太弱
sys.exit("key A can't be 1.Choose a different key.")
elif keyB == 0 and mode == 'encrypt': #keyB 为 0 时,秘钥太弱
sys.exit("key B can't be 0.Choose a different key.")
elif keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) -1:#限定秘钥范围。其中keyA 大于0 , 0 < keyB < len(SYMBOLS)
sys.exit('Key A must be greater than 0 and key B must be between 0 and %s')%(len(SYMBOLS))
elif cryptomath.gcd(keyA , len(SYMBOLS)) != 1: #前面说秘钥keyA 必须与符号集的大小互质
sys.exit('key A and the symbol set size are not relatively prime.Choose a different key')
#加密解密过程均是找到对应关系
def encryptMessage(key , message):
keyA , keyB = getKeyParts(key)
checkKeys(keyA, keyB , 'encrypt')
cipherText = ''
for symbol in message:
if symbol in SYMBOLS:
symIndex = SYMBOLS.find(symbol)
cipherText += SYMBOLS[(symIndex * keyA + keyB) % len(SYMBOLS)]
else:
cipherText += symbol
return cipherText def decryptMessage(key , message):
keyA , keyB = getKeyParts(key)
checkKeys(keyA, keyB , 'decrypt')
plainText = ''
modInverseOfKeyA = cryptomath.findModInverse(keyA , len(SYMBOLS)) for symbol in message:
if symbol in SYMBOLS:
symIndex = SYMBOLS.find(symbol)
plainText += SYMBOLS[(symIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
else:
plainText += symbol
return plainText def getRandomKey():#可能不容易想到一个符合条件的秘钥。这时你可以选择用getRandomKey() 函数自动生成一个秘钥
while True:
keyA = random.randint(2 , len(SYMBOLS))
keyB = random.randint(2 , len(SYMBOLS))
if cryptomath.gcd(keyA , len(SYMBOLS)) == 1:
return keyA * len(SYMBOLS) + keyB if __name__ == '__main__':
main()
4.仿射加密法的破译
仿射加密法的秘钥可能数是有限的
SYMBOLS = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
对于上述这个字符集。(len(SYMBOLS) = 95)。
因为秘钥 keyB 是对这个长度取模得到的。所以 keyB 的值在 0 和这个 长度减一 之间。 keyB的可能取值是95个
对于秘钥keyA 。我们假设一个字母原来对应的数字为 X , 加密后对应的数字是 Y。
即。 Y = (X * keyA + keyB)%len(SYMBOLS)
那么。我们同样有 (X * (keyA+len(SYMBOLS)) +keyB ) % len(SYMBOLS) = (X * keyA + keyB)%len(SYMBOLS) + x*len(SYMBOLS) % len(SYMBOLS) = Y
这也就是说。对于keyA ,我们取 keyA + len(SYMBOLS) 作为新的秘钥 keyA 。我们得到的加密消息将会是一样的。
这意味着。秘钥A 和 B都受限于符号集的大小。
affineHacker.py
import affineCipher ,detectEnglish , cryptomath
SILENT_MODE = False def main():
myMessage = """U&'<3dJ^Gjx'-3^MS'Sj0jxuj'G3'%j'<mMMjS'g{GjMMg9j{G'g"'gG'<3^MS'Sj<jguj'm'P^dm{'g{G3'%jMgjug{9'GPmG'gG'-m0'P^dm{LU'5&Mm{'_^xg{9"""
hackedMessage = hackAffine(myMessage) if hackedMessage != None:
print hackedMessage
else:
print 'Failed to hack encryption.' def hackAffine(message):
print 'Hacking ...'
for key in xrange(len(affineCipher.SYMBOLS) ** 2):
keyA = affineCipher.getKeyParts(key)[0]
if cryptomath.gcd(keyA , len(affineCipher.SYMBOLS)) != 1:
continue
decryptedText = affineCipher.decryptMessage(key , message)
if not SILENT_MODE:
print 'Tried key %s ... (%s)'%(key , decryptedText) if detectEnglish.isEnglish(decryptedText):
print ''
print 'Possible encryption hack :'
print 'Key " %s'%(key)
print 'Decrypted message :'+decryptedText[:200]
print ''
print 'Enter D for done , or just press Enter to continue hacking'
resp = raw_input('>>>')
if resp.strip().upper().startswith('D'):
return decryptedText
return None if __name__ == '__main__':
main()
由前面的分析。我们得到。keyA 和 keyB 的值均在 1 到 len(SYMBOLS)之间。
那么,我们对应的秘钥就可能有 len(SYMBOLS) * len(SYMBOLS) 个。
我们遍历这些可能的秘钥。通过上一个py文件得到 keyA keyB 的值然后尝试解密。得到英文消息时自动暂停。
5.简单替代加密法
要实现简单替代加密法,随机选择字母来加密字母表的每个字母。每个字母只用一次。这样的秘钥可以有 403 291 461 126 605 635 584 000 000 个。
类似于前面的凯撒加密法和仿射加密法。 只不过,这一次,我们并不用简单的加或乘的关系来找对应的秘钥。
simpleSubCipher.py
import sys , random LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' def main():
myMessage = 'If a man is offered a fact which goes against his instincts, he will scrutinize it closely, and unless the evidence is overwhelming, he will refuse to believe it. If, on the other hand, he is offered something which affords a reason for acting in accordance to his instincts, he will accept it even on the slightest evidence. The origin of myths is explained in this way. -Bertrand Russell'
myKey = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'
myMode = 'encrypt' checkValidKey(myKey) if myMode == 'encrypt':
translated = encryptMessage(myKey , myMessage)
elif myMode == 'decrypt':
translated = decryptMessage(myKey , myMessage)
print 'Using key %s'%(myKey)
print ''
print 'The %sed message is : '%(myMode)
print translated
print ''
print 'the decrypted message is :'
print decryptMessage(myKey , translated) def checkValidKey(key):
keyList = list(key)
lettersList = list(LETTERS)
keyList.sort()
lettersList.sort()
if keyList != lettersList:
sys.exit('There is an error in the key or symbol set.') def encryptMessage(key , message):
return translateMessage(key , message , 'encrypt')
def decryptMessage(key , message):
return translateMessage(key , message , 'decrypt') def translateMessage(key , message , mode):
translated = ''
charsA = LETTERS
charsB = key
if mode == 'encrypt':
charsA , charsB = charsB , charsA
for symbol in message:
if symbol.upper() in charsA:
symIndex = charsA.find(symbol.upper())
if symbol.isupper():
translated += charsB[symIndex]
else:
translated += charsB[symIndex].lower()
else:
translated += symbol
return translated def getRandomKey():
key = list(LETTERS)
random.shuffle(key)
return ''.join(key) if __name__ == '__main__':
main()
我们设置一个秘钥。这个秘钥应当是26个英文字母的某种排列方式。我们将这种设定的排列方式和原顺序(ABCDEFG。。。)对应起来进行替换。
怎么破译简单替代加密法。?
前面我们说到。简单替代加密法可能的秘钥有 403 291 461 126 605 635 584 000 000 个。秘钥实在太多了,无法让计算机来暴力破解。
我们来看一下一个可能的密文单词 HGHHU。
虽然我们不知道它对应的明文是什么。但是我们可以知道一点关于明文的信息:
1. 明文单词一定是 5 个字母
2. 明文第一,第三,第四个字母是一样的
3. 这个单词有 3 个不同的字母,第一,第二,第五个字母彼此不同
那我们可以从字典中找到同样符合这三个特性的单词。例如 Puppy Mommy Bobby Lilly
现在。我们需要给密词创建单词模式。第一个字母获得数字0 , 后面每个不同的字母第一次出现时都会获得下一个数字。比如:
cat 的单词模式为 0,1,2
catty 的单词模式为 0,1,2,2,3
roofer 的单词模式为 0,1,1,2,3,0
那么。我们说,明文单词和密文单词总是有相同的单词模式。不管你使用的是什么秘钥。
我们先写一个小程序来计算字典文件中每个单词的单词模式,并且保存在本地
wordPatterns.py
#-*-coding:utf-8-*-
import pprint
def getWordPattern(word): #返回单词模式
word = word.upper()
nextNum = 0
letterNums = {}
wordPattern = [] for letter in word:
if letter not in letterNums:
letterNums[letter] = str(nextNum)
nextNum += 1
wordPattern.append(letterNums[letter])
return '.'.join(wordPattern) def main():
allPatterns = {}
fo = open('dictionary.txt')
wordList = fo.read().split('\n')
fo.close() for word in wordList:
pattern = getWordPattern(word) if pattern not in allPatterns:
allPatterns[pattern] = [word]
else:
allPatterns[pattern].append(word) fo = open('wordPatterns.py','w')
fo.write('allPatterns = ')
fo.write(pprint.pformat(allPatterns))
fo.close() if __name__ == '__main__':
main()
这个程序会在本地生成一个 wordPatterns.py 文件。里面以字典的形式保存了各种不同的单词模式所对应的单词。你可以用编辑器打开看一下。
python 密码学编程 -- 2的更多相关文章
- python 密码学编程
最近在看一本书.名字是 python密码学编程.在此做一些笔记,同时也为有需要的人提供一些参考. *************************************************** ...
- 【python密码学编程】5.反转加密法
#Reverse Cipher message = 'there can keep a secret,if two of them are dead.' translated = '' i = len ...
- 【python密码学编程】8.使用换位加密法加密
替代加密法:用其他字符替代原有字符 换位加密法:搞乱字符顺序 [换位加密法]需要一个密钥 仅允许非商业转载,转载请注明出处
- 【python密码学编程】7.暴力破解凯撒加密法
# _*_ coding:utf-8 _*_ #Caeser Ciper import pyperclip messgae = 'GUVF VF ZL FRPERG ZRFFTNR.' nums = ...
- 【python密码学编程】6.凯撒加密法
凯撒加密法的迷药是0~25的整数 # _*_ coding:utf-8 _*_ #Caeser Ciper import pyperclip messgae = 'this is my secret ...
- 《Python游戏编程快速上手》|百度网盘免费下载|Python基础编程
<Python游戏编程快速上手>|百度网盘免费下载| 提取码:luy6 Python是一种高级程序设计语言,因其简洁.易读及可扩展性日渐成为程序设计领域备受推崇的语言. 本书通过编写一个个 ...
- Python Socket 编程——聊天室示例程序
上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...
- python多线程编程
Python多线程编程中常用方法: 1.join()方法:如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行,那么在调用线程的时就可以使用被调线程的join方法join( ...
- python 面向对象编程学习
1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...
随机推荐
- Volley源码分析一
Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...
- 10亿美元融资腾讯跟头,Grail要用基因测序做癌症早期筛查
癌症超早期筛查:"在干草堆中寻找缝衣针"癌症是人类的噩梦,尤其是中晚期癌症,但很多时候,当患者感觉到身体不适而去医院检查时,病情都已经到了中晚期,很难治愈.而有研究表明,早期癌症患 ...
- react实现双向绑定
双向绑定功能是在项目中比较常见的一个需求,传统的js实现方式是添加监听函数的方式,Vue框架实现很简单,因为它本身就是基于双向绑定实现的,接下来我将讲解一下如何使用react实现双向绑定的功能是 首先 ...
- MySQL left join操作中 on与where放置条件的区别
优先级 两者放置相同条件,之所以可能会导致结果集不同,就是因为优先级.on的优先级是高于where的. 1 1 首先明确两个概念: LEFT JOIN 关键字会从左表 (table_name1) 那里 ...
- 【Android Developers Training】 10. 序言:支持不同设备
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 手机端 图片的移动缩放旋转兼容touch
//缩放var initialScale = 1;var currentScale = 1;touch.on('#target', 'pinch', function (ev) { currentSc ...
- 探索Windows命令行系列(7):通过命令编译C#类和Java类
1.编译 C# 类 1.1.C# 编译工具 1.2.编译一个 C# 类 1.3.编译多个 C# 类 2.编译 Java 类 2.1.Java 编译工具 2.2.编译 Java 类 3.组合命令符 4. ...
- 基于GCC的openMP学习与测试(2)
一.openMP简单测试 1.简单测试(1) #include<omp.h> #include<time.h> #include<iostream> using n ...
- 游戏UI框架设计(7): 资源国际化技术
游戏UI框架设计(7) --资源国际化技术 说起"资源国际化"技术,个人认为可以追述到微软Window2000 PC操作系统的发布,在这之前windows98操作系统的开发都是先由 ...
- Linux内核互斥锁--mutex
一.定义: /linux/include/linux/mutex.h 二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见 ...