接上一篇随笔

********************************************************************

*        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的更多相关文章

  1. python 密码学编程

    最近在看一本书.名字是 python密码学编程.在此做一些笔记,同时也为有需要的人提供一些参考. *************************************************** ...

  2. 【python密码学编程】5.反转加密法

    #Reverse Cipher message = 'there can keep a secret,if two of them are dead.' translated = '' i = len ...

  3. 【python密码学编程】8.使用换位加密法加密

    替代加密法:用其他字符替代原有字符 换位加密法:搞乱字符顺序 [换位加密法]需要一个密钥 仅允许非商业转载,转载请注明出处

  4. 【python密码学编程】7.暴力破解凯撒加密法

    # _*_ coding:utf-8 _*_ #Caeser Ciper import pyperclip messgae = 'GUVF VF ZL FRPERG ZRFFTNR.' nums = ...

  5. 【python密码学编程】6.凯撒加密法

    凯撒加密法的迷药是0~25的整数 # _*_ coding:utf-8 _*_ #Caeser Ciper import pyperclip messgae = 'this is my secret ...

  6. 《Python游戏编程快速上手》|百度网盘免费下载|Python基础编程

    <Python游戏编程快速上手>|百度网盘免费下载| 提取码:luy6 Python是一种高级程序设计语言,因其简洁.易读及可扩展性日渐成为程序设计领域备受推崇的语言. 本书通过编写一个个 ...

  7. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  8. python多线程编程

    Python多线程编程中常用方法: 1.join()方法:如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行,那么在调用线程的时就可以使用被调线程的join方法join( ...

  9. python 面向对象编程学习

    1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...

随机推荐

  1. mysql的my.ini文件详解

    mysql数据库在配置时包含很多信息:端口号,字符编码,指定根路径 basedir,指定数据存放的路径等信息 mysql的字体编码分为两种: 服务器编码 客户端输入的编码 通常服务器的编码都是utf- ...

  2. Linux下memcached安装与连接

    前几天技术总监要我在项目中加一个memcached,以前也从来没有配置过,所以就去网上找教程,最终折腾成功.比较坑的就是sasl协议那里. 由于memcached依赖libevents,所以要下载两个 ...

  3. 我的学习之路_第二十一章_JDBC连接池

    JDBC连接池和DButils [DBCP连接池工具类] 使用读取配置文件的方式 DBCP中有一个工厂类 BasicDataSourceFactory 工厂类中有一个静态方法 返回值为: DataSo ...

  4. Mybatis中使用 #{} 和 ${} 向sql传参时的区别

    今天在工作时,使用MyBatis中向sql传递两个参数时,一直显示SQL语法错误,仔细检查,才发现传入的参数被加上了引号,导致传入的参数(要传入的参数是表名)附近出现语法错误. 错误写法: } a } ...

  5. 第14章 Linux开机详细流程

    本文目录: 14.1 按下电源和bios阶段 14.2 MBR和各种bootloader阶段 14.2.1 boot loader 14.2.2 分区表 14.2.3 采用VBR/EBR方式引导操作系 ...

  6. 基于HTTP协议的下载功能实现

    超文本传输协议 (HTTP-HyperText Transfer Protocol)是一种使用极为广泛的协议,它由请求和响应构成,是一种无状态的应用层协议.设计HTTP协议的初衷是为了提供一种传输HT ...

  7. Tomcat常用配置修改

    Tomcat常用配置修改 说明 运行需要设置环境变量 JAVA_HOME 即JDK安装目录 tomcat 默认登录地址 http://localhost:8080 配置tomcat 1.端口设置 打开 ...

  8. 正确、安全地停止SpringBoot应用服务

    引言 Spring Boot,作为Spring框架对"约定优先于配置(Convention Over Configuration)"理念的最佳实践的产物,它能帮助我们很快捷的创建出 ...

  9. Linux - 请允许我静静地后台运行

    h1,h2,h3,h4,h5,h6,p,blockquote { margin: 0; padding: 0 } body { font-family: "Helvetica Neue&qu ...

  10. eclipse 小方法