字母种类数量相同,但顺序被打乱了
在简单的纵行换位密码中,明文以固定的宽度水平的写在一张图表纸上,密文按垂直方向读出,解密就是密文按相同的宽度垂直的写在图表纸上,然后水平的读出明文。
加密步骤:
- 数一下消息里的字符数
- 画一个行数等于密钥的格子
- 从左到右填充格子
- 当用完格子却还有字符时再画一行格子
- 从最上角开始向下读字符,到底后向右移一行
1 # Transposition Cipher Encryption 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 import pyperclip 5 6 def main(): 7 myMessage = 'Common sense is not so common.' 8 myKey = 8 9 10 ciphertext = encryptMessage(myKey, myMessage) 11 12 # Print the encrypted string in ciphertext to the screen, with 13 # a | (called "pipe" character) after it in case there are spaces at 14 # the end of the encrypted message. 15 print(ciphertext + '|') 16 17 # Copy the encrypted string in ciphertext to the clipboard. 18 pyperclip.copy(ciphertext) 19 20 21 def encryptMessage(key, message): 22 # Each string in ciphertext represents a column in the grid. 23 ciphertext = [''] * key 24 25 # Loop through each column in ciphertext. 26 for col in range(key): 27 pointer = col 28 29 # Keep looping until pointer goes past the length of the message. 30 while pointer < len(message): 31 # Place the character at pointer in message at the end of the 32 # current column in the ciphertext list. 33 ciphertext[col] += message[pointer] 34 35 # move pointer over 36 pointer += key 37 38 # Convert the ciphertext list into a single string value and return it. 39 return ''.join(ciphertext) 40 41 42 # If transpositionEncrypt.py is run (instead of imported as a module) call 43 # the main() function. 44 if __name__ == '__main__': 45 main()
解密步骤:
- 将消息长度除以密钥并向上取整得到列数
- 画出格子,行数与密钥一样
- 填入密文,从左上角开始,从左向右
- 从最左边一列开始,从上向下读
1 # Transposition Cipher Decryption 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 import math, pyperclip 5 6 def main(): 7 myMessage = 'Cenoonommstmme oo snnio. s s c' 8 myKey = 8 9 10 plaintext = decryptMessage(myKey, myMessage) 11 12 # Print with a | (called "pipe" character) after it in case 13 # there are spaces at the end of the decrypted message. 14 print(plaintext + '|') 15 16 pyperclip.copy(plaintext) 17 18 19 def decryptMessage(key, message): 20 # The transposition decrypt function will simulate the "columns" and 21 # "rows" of the grid that the plaintext is written on by using a list 22 # of strings. First, we need to calculate a few values. 23 24 # The number of "columns" in our transposition grid: 25 numOfColumns = math.ceil(len(message) / key) 26 # The number of "rows" in our grid will need: 27 numOfRows = key 28 # The number of "shaded boxes" in the last "column" of the grid: 29 numOfShadedBoxes = (numOfColumns * numOfRows) - len(message) 30 31 # Each string in plaintext represents a column in the grid. 32 plaintext = [''] * numOfColumns 33 34 # The col and row variables point to where in the grid the next 35 # character in the encrypted message will go. 36 col = 0 37 row = 0 38 39 for symbol in message: 40 plaintext[col] += symbol 41 col += 1 # point to next column 42 43 # If there are no more columns OR we're at a shaded box, go back to 44 # the first column and the next row. 45 if (col == numOfColumns) or (col == numOfColumns - 1 and row >= numOfRows - numOfShadedBoxes): 46 col = 0 47 row += 1 48 49 return ''.join(plaintext) 50 51 52 # If transpositionDecrypt.py is run (instead of imported as a module) call 53 # the main() function. 54 if __name__ == '__main__': 55 main()
测试程序
生成随机字符串加密再解密,二者比较
1 # Transposition Cipher Test 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 import random, sys, transpositionEncrypt, transpositionDecrypt 5 6 def main(): 7 random.seed(42) # set the random "seed" to a static value 8 9 for i in range(20): # run 20 tests 10 # Generate random messages to test. 11 12 # The message will have a random length: 13 message = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' * random.randint(4, 40) 14 15 # Convert the message string to a list to shuffle it. 16 message = list(message) 17 random.shuffle(message) 18 message = ''.join(message) # convert list to string 19 20 print('Test #%s: "%s..."' % (i+1, message[:50])) 21 22 # Check all possible keys for each message. 23 for key in range(1, len(message)): 24 encrypted = transpositionEncrypt.encryptMessage(key, message) 25 decrypted = transpositionDecrypt.decryptMessage(key, encrypted) 26 27 # If the decryption doesn't match the original message, display 28 # an error message and quit. 29 if message != decrypted: 30 print('Mismatch with key %s and message %s.' % (key, message)) 31 print(decrypted) 32 sys.exit() 33 34 print('Transposition cipher test passed.') 35 36 37 # If transpositionTest.py is run (instead of imported as a module) call 38 # the main() function. 39 if __name__ == '__main__': 40 main()
加密文本文件:
1 # Transposition Cipher Encrypt/Decrypt File 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 import time, os, sys, transpositionEncrypt, transpositionDecrypt 5 6 def main(): 7 inputFilename = 'frankenstein.txt' 8 # BE CAREFUL! If a file with the outputFilename name already exists, 9 # this program will overwrite that file. 10 outputFilename = 'frankenstein.encrypted.txt' 11 myKey = 10 12 myMode = 'encrypt' # set to 'encrypt' or 'decrypt' 13 14 # If the input file does not exist, then the program terminates early. 15 if not os.path.exists(inputFilename): 16 print('The file %s does not exist. Quitting...' % (inputFilename)) 17 sys.exit() 18 19 # If the output file already exists, give the user a chance to quit. 20 if os.path.exists(outputFilename): 21 print('This will overwrite the file %s. (C)ontinue or (Q)uit?' % (outputFilename)) 22 response = input('> ') 23 if not response.lower().startswith('c'): 24 sys.exit() 25 26 # Read in the message from the input file 27 fileObj = open(inputFilename) 28 content = fileObj.read() 29 fileObj.close() 30 31 print('%sing...' % (myMode.title())) 32 33 # Measure how long the encryption/decryption takes. 34 startTime = time.time() 35 if myMode == 'encrypt': 36 translated = transpositionEncrypt.encryptMessage(myKey, content) 37 elif myMode == 'decrypt': 38 translated = transpositionDecrypt.decryptMessage(myKey, content) 39 totalTime = round(time.time() - startTime, 2) 40 print('%sion time: %s seconds' % (myMode.title(), totalTime)) 41 42 # Write out the translated message to the output file. 43 outputFileObj = open(outputFilename, 'w') 44 outputFileObj.write(translated) 45 outputFileObj.close() 46 47 print('Done %sing %s (%s characters).' % (myMode, inputFilename, len(content))) 48 print('%sed file is %s.' % (myMode.title(), outputFilename)) 49 50 51 # If transpositionCipherFile.py is run (instead of imported as a module) 52 # call the main() function. 53 if __name__ == '__main__': 54 main()
破解时仍是暴力破解,让计算机识别英文,方法是找一个字典,让程序将得到的字符串与字典内容比较,若一句话里的词在字典内出现的比率达到一定值计算机将其视为英语,让人判断
1 # Detect English module 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 # To use, type this code: 5 # import detectEnglish 6 # detectEnglish.isEnglish(someString) # returns True or False 7 # (There must be a "dictionary.txt" file in this directory with all English 8 # words in it, one word per line. You can download this from 9 # http://invpy.com/dictionary.txt) 10 UPPERLETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 11 LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n' 12 13 def loadDictionary(): 14 dictionaryFile = open('dictionary.txt') 15 englishWords = {} 16 for word in dictionaryFile.read().split('\n'): 17 englishWords[word] = None 18 dictionaryFile.close() 19 return englishWords 20 21 ENGLISH_WORDS = loadDictionary() 22 23 24 def getEnglishCount(message): 25 message = message.upper() 26 message = removeNonLetters(message) 27 possibleWords = message.split() 28 29 if possibleWords == []: 30 return 0.0 # no words at all, so return 0.0 31 32 matches = 0 33 for word in possibleWords: 34 if word in ENGLISH_WORDS: 35 matches += 1 36 return float(matches) / len(possibleWords) 37 38 39 def removeNonLetters(message): 40 lettersOnly = [] 41 for symbol in message: 42 if symbol in LETTERS_AND_SPACE: 43 lettersOnly.append(symbol) 44 return ''.join(lettersOnly) 45 46 47 def isEnglish(message, wordPercentage=20, letterPercentage=85): 48 # By default, 20% of the words must exist in the dictionary file, and 49 # 85% of all the characters in the message must be letters or spaces 50 # (not punctuation or numbers). 51 wordsMatch = getEnglishCount(message) * 100 >= wordPercentage 52 numLetters = len(removeNonLetters(message)) 53 messageLettersPercentage = float(numLetters) / len(message) * 100 54 lettersMatch = messageLettersPercentage >= letterPercentage 55 return wordsMatch and lettersMatch
破解即为暴力破解:
1 # Detect English module 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 # To use, type this code: 5 # import detectEnglish 6 # detectEnglish.isEnglish(someString) # returns True or False 7 # (There must be a "dictionary.txt" file in this directory with all English 8 # words in it, one word per line. You can download this from 9 # http://invpy.com/dictionary.txt) 10 UPPERLETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 11 LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n' 12 13 def loadDictionary(): 14 dictionaryFile = open('dictionary.txt') 15 englishWords = {} 16 for word in dictionaryFile.read().split('\n'): 17 englishWords[word] = None 18 dictionaryFile.close() 19 return englishWords 20 21 ENGLISH_WORDS = loadDictionary() 22 23 24 def getEnglishCount(message): 25 message = message.upper() 26 message = removeNonLetters(message) 27 possibleWords = message.split() 28 29 if possibleWords == []: 30 return 0.0 # no words at all, so return 0.0 31 32 matches = 0 33 for word in possibleWords: 34 if word in ENGLISH_WORDS: 35 matches += 1 36 return float(matches) / len(possibleWords) 37 38 39 def removeNonLetters(message): 40 lettersOnly = [] 41 for symbol in message: 42 if symbol in LETTERS_AND_SPACE: 43 lettersOnly.append(symbol) 44 return ''.join(lettersOnly) 45 46 47 def isEnglish(message, wordPercentage=20, letterPercentage=85): 48 # By default, 20% of the words must exist in the dictionary file, and 49 # 85% of all the characters in the message must be letters or spaces 50 # (not punctuation or numbers). 51 wordsMatch = getEnglishCount(message) * 100 >= wordPercentage 52 numLetters = len(removeNonLetters(message)) 53 messageLettersPercentage = float(numLetters) / len(message) * 100 54 lettersMatch = messageLettersPercentage >= letterPercentage 55 return wordsMatch and lettersMatch
破解加密文本文件(我记得书上没看见这个例子,拓展题???):
1 # Transposition File Hacker 2 # http://inventwithpython.com/hacking (BSD Licensed) 3 4 import sys, time, os, sys, transpositionDecrypt, detectEnglish 5 6 inputFilename = 'frankenstein.encrypted.txt' 7 outputFilename = 'frankenstein.decrypted.txt' 8 9 def main(): 10 if not os.path.exists(inputFilename): 11 print('The file %s does not exist. Quitting.' % (inputFilename)) 12 sys.exit() 13 14 inputFile = open(inputFilename) 15 content = inputFile.read() 16 inputFile.close() 17 18 brokenMessage = hackTransposition(content) 19 20 if brokenMessage != None: 21 print('Writing decrypted text to %s.' % (outputFilename)) 22 23 outputFile = open(outputFilename, 'w') 24 outputFile.write(brokenMessage) 25 outputFile.close() 26 else: 27 print('Failed to hack encryption.') 28 29 30 # The hackTransposition() function's code was copy/pasted from 31 # transpositionHacker.py and had some modifications made. 32 def hackTransposition(message): 33 print('Hacking...') 34 # Python programs can be stopped at any time by pressing Ctrl-C (on 35 # Windows) or Ctrl-D (on Mac and Linux) 36 print('(Press Ctrl-C or Ctrl-D to quit at any time.)') 37 38 for key in range(1, len(message)): 39 print('Trying key #%s... ' % (key), end='') 40 sys.stdout.flush() 41 42 # We want to track the amount of time it takes to test a single key, 43 # so we record the time in startTime. 44 startTime = time.time() 45 46 decryptedText = transpositionDecrypt.decryptMessage(key, message) 47 englishPercentage = round(detectEnglish.getEnglishCount(decryptedText) * 100, 2) 48 49 totalTime = round(time.time() - startTime, 3) 50 print('Test time: %s seconds, ' % (totalTime), end='') 51 sys.stdout.flush() # flush printed text to the screen 52 53 print('Percent English: %s%%' % (englishPercentage)) 54 if englishPercentage > 20: 55 print() 56 print('Key ' + str(key) + ': ' + decryptedText[:100]) 57 print() 58 print('Enter D for done, or just press Enter to continue:') 59 response = input('> ') 60 if response.strip().upper().startswith('D'): 61 return decryptedText 62 return None 63 64 65 # If transpositionFileHacker.py is run (instead of imported as a module) 66 # call the main() function. 67 if __name__ == '__main__': 68 main()