破解替代加密法

思路

首先必須先找出字典文件裏的每個單詞的模式,把它們放置在一個列表裏排序好,以便獲取匹配特定密詞的單詞模式的所有候選單詞。
計算字典文件裏的每個單詞的單詞模式,保存到另一個文件裏,及 wordPatterns.py文件。
計算單詞模式的代碼是 makeWordPatterns.py

理論步驟

1.找出密文裏的每個密詞的單詞模式
2.找出每個密詞可以解密成哪些英文單詞
3.使用密詞的候選單詞列表爲每個密詞創建一個密字映射
4.計算所有密字映射的交集,得到一個交集密字映射
5.從交集密字映射移除任何已經破譯的字母

密文消息越長,越可能破譯

makeWordPatterns.py

import pprint

def getWordPattern(word):
    # 計算單詞模式
    # 例如 輸入'DUSTBUSTER' 返回'0.1.2.3.4.1.2.3.5.6'  
    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()

破解實例

import os, re, copy, pprint, simpleSubCipher, makeWordPatterns

if not os.path.exists('wordPatterns.py'):
    makeWordPatterns.main() # 如果目錄下沒有wordPatterns.py文件則調用創建
import wordPatterns

LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
nonLettersOrSpacePattern = re.compile('[^A-Z\s]') # 正則匹配任何不適A到Z的字母也不是空白字符的字符


def getBlankCipherletterMapping():
    # 返回密字映射字典,值是單個大寫字母字符串的列表
    return {'A': [], 'B': [], 'C': [], 'D': [], 'E': [], 'F': [], 'G': [], 'H': [], 'I': [], 'J': [], 'K': [], 'L': [], 'M': [], 'N': [], 'O': [], 'P': [], 'Q': [], 'R': [], 'S': [], 'T': [], 'U': [], 'V': [], 'W': [], 'X': [], 'Y': [], 'Z': []}


def addLettersToMapping(letterMapping, cipherword, candidate):
    # 將每個單詞拆分成單個字符添加到映射字典裏
    letterMapping = copy.deepcopy(letterMapping) # 深度複製 防止誤操作數據 創建一個臨時映射字典
    for i in range(len(cipherword)):
        # 將每個單詞對應的映射字符放到一個臨時的映射字典裏
        if candidate[i] not in letterMapping[cipherword[i]]:
            letterMapping[cipherword[i]].append(candidate[i])
    return letterMapping


def intersectMappings(mapA, mapB):
    # 將臨時映射字典彙總到最終的映射字典裏
    intersectedMapping = getBlankCipherletterMapping()
    for letter in LETTERS:
        # 如果總字典是空,則直接複製當前字典;如果臨時字典是空,則總字典複製其本身;
        if mapA[letter] == []:
            intersectedMapping[letter] = copy.deepcopy(mapB[letter])
        elif mapB[letter] == []:
            intersectedMapping[letter] = copy.deepcopy(mapA[letter])
        else:
            # 如果都不爲空,則將臨時字典裏的數據添加到總字典裏
            for mappedLetter in mapA[letter]:
                if mappedLetter in mapB[letter]:
                    intersectedMapping[letter].append(mappedLetter)
    return intersectedMapping


def removeSolvedLettersFromMapping(letterMapping):
    # 剔除映射字典裏已經破譯的數據
    letterMapping = copy.deepcopy(letterMapping)
    loopAgain = True
    while loopAgain:
        # 由於剔除需要循環剔除,如果沒有剔除完全則會繼續循環,先關閉下面判斷再進行開啓
        loopAgain = False
        # 已經破解出的字符放入到solvedletters裏面
        solvedLetters = []
        for cipherletter in LETTERS:
            if len(letterMapping[cipherletter]) == 1:
                # 已經是當個對應的字符則是已破譯
                solvedLetters.append(letterMapping[cipherletter][0])
        # 判斷,如果映射字典裏的值不是一個,並且已破譯也在當中,則將映射字典值剔除此破譯字符
        for cipherletter in LETTERS:
            for s in solvedLetters:
                if len(letterMapping[cipherletter]) != 1 and s in letterMapping[cipherletter]:
                    letterMapping[cipherletter].remove(s)
                    if len(letterMapping[cipherletter]) == 1:
                        # 如果當前值已經等於1個則進行剔除這個破譯字符
                        loopAgain = True
    return letterMapping


def hackSimpleSub(message):
    intersectedMap = getBlankCipherletterMapping()
    cipherwordList = nonLettersOrSpacePattern.sub('', message.upper()).split()
    for cipherword in cipherwordList:
        # 對密文列表進行處理,計算單詞模式,獲取對應模式的單詞
        newMap = getBlankCipherletterMapping()
        wordPattern = makeWordPatterns.getWordPattern(cipherword)
        if wordPattern not in wordPatterns.allPatterns:
            continue # 如果計算出的模式在單詞裏沒有找到則下一組密文
        # 如果找到對應的單詞,將其拆分添加到映射字典裏
        for candidate in wordPatterns.allPatterns[wordPattern]:
            newMap = addLettersToMapping(newMap, cipherword, candidate)
        # 將所有臨時映射字典彙總到總的映射字典裏
        intersectedMap = intersectMappings(intersectedMap, newMap)

    # 剔除已經破譯的映射字符
    return removeSolvedLettersFromMapping(intersectedMap)


def decryptWithCipherletterMapping(ciphertext, letterMapping):
    # 用已解得的密字映射字典翻譯密文
    key = ['x'] * len(LETTERS) 
    for cipherletter in LETTERS:
        if len(letterMapping[cipherletter]) == 1:
            # 如果映射字典的值是一個,那麼就加入到密鑰中
            keyIndex = LETTERS.find(letterMapping[cipherletter][0])
            key[keyIndex] = cipherletter
        else:
            # 如果映射值不只一個或沒有,那麼算是未破解,用下劃線代替加入到密鑰中
            ciphertext = ciphertext.replace(cipherletter.lower(), '_')
            ciphertext = ciphertext.replace(cipherletter.upper(), '_')
    key = ''.join(key)
    # 調用簡單替代法解密方法來翻譯密文
    return simpleSubCipher.decryptMessage(key, ciphertext)

def main():
    message = 'Sy l nlx sr pyyacao l ylwj eiswi upar lulsxrj isr sxrjsxwjr, ia esmm rwctjsxsza sj wmpramh, lxo txmarr jia aqsoaxwa sr pqaceiamnsxu, ia esmm caytra jp famsaqa sj. Sy, px jia pjiac ilxo, ia sr pyyacao rpnajisxu eiswi lyypcor l calrpx ypc lwjsxu sx lwwpcolxwa jp isr sxrjsxwjr, ia esmm lwwabj sj aqax px jia rmsuijarj aqsoaxwa. Jia pcsusx py nhjir sr agbmlsxao sx jisr elh. -Facjclxo Ctrramm'
    print('Hacking...')
    # 解密映射字典
    letterMapping = hackSimpleSub(message)
    print('Mapping:')
    # 輸出映射字典
    pprint.pprint(letterMapping)
    print()
    print('Original ciphertext:')
    print(message)
    print()
    # 翻譯密文成明文
    hackedMessage = decryptWithCipherletterMapping(message, letterMapping)
    print(hackedMessage)



if __name__ == '__main__':
    main()

運行結果

運行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章