簡單替代密碼是一種以單個字母替換另一個字母的加密方式。破解此類別密碼的關鍵在於分析密鑰中的詞模式,並利用已知的詞彙表比對可能的明文。詞模式指的是單詞中字母的相對位置,例如 helloworld 的詞模式分別是 0.1.2.2.30.1.2.3.4。透過詞模式匹配,我們可以快速縮小候選明文的範圍。接著,我們需要建立字母對映表,將密鑰字母與可能的明文字母關聯起來。透過多個密鑰詞的對映表交集,可以逐步確定每個密鑰字母對應的明文字母,最終還原出明文訊息。

在 Python 中,我們可以利用 wordPatterns 模組建立詞模式字典,並使用字典和列表等資料結構來儲存和操作字母對映表。程式碼中,getBlankCipherletterMapping() 函式用於建立初始的空白對映表,addLettersToMapping() 函式則根據候選明文更新對映表。intersectMappings() 函式負責計算多個對映表的交集,removeSolvedLettersFromMapping() 函式則用於移除已確定的字母對映,簡化對映表。最後,decryptWithCipherletterMapping() 函式根據最終的對映表解密密鑰。

簡單替代密碼破解技術深度解析

前言

簡單替代密碼是一種基礎的加密技術,其破解過程涉及多個抽象概念,如「單詞模式」和「密碼字母對映」。本篇文章將探討這些概念及其在Python程式中的實作。

單詞模式與密碼字母對映

在破解簡單替代密碼的過程中,「單詞模式」用於表示單詞中字母的相對位置,而「密碼字母對映」則用於跟蹤每個密碼字母可能的解密結果。這些概念在程式中分別以字串和字典的形式呈現。

程式碼解析

# 簡單替代密碼破解程式
import os, re, copy, pprint, pyperclip, simpleSubCipher, makeWordPatterns

# 檢查wordPatterns.py檔案是否存在,若不存在則建立
if not os.path.exists('wordPatterns.py'):
    makeWordPatterns.main() 
import wordPatterns

LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
nonLettersOrSpacePattern = re.compile('[^A-Z\s]')

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()
    print('Copying hacked message to clipboard:')
    hackedMessage = decryptWithCipherletterMapping(message, letterMapping)
    pyperclip.copy(hackedMessage)
    print(hackedMessage)

#### 內容解密:
此段程式碼為簡單替代密碼破解的主函式首先檢查必要的`wordPatterns.py`檔案是否存在若不存在則呼叫`makeWordPatterns.main()`建立然後定義了一個密鑰範例並呼叫`hackSimpleSub`函式進行破解最後輸出破解結果並將其複製到剪貼簿

### 密碼字母對映的建立與更新

```python
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

#### 內容解密:
`getBlankCipherletterMapping`函式用於建立一個空白的密碼字母對映字典每個密碼字母初始對應一個空列表。`addLettersToMapping`函式則根據給定的密鑰字母和可能的明文字母更新密碼字母對映若候選明文字母不在當前密鑰字母的對映列表中則將其新增至列表

對映交集與簡化

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 = []
        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:
                loopAgain = True
    return letterMapping

#### 內容解密:
`intersectMappings`函式用於計算兩個密碼字母對映的交集以找出共同可能的解密字母。`removeSolvedLettersFromMapping`函式則透過移除已確定的解密字母來簡化密碼字母對映從而進一步縮小可能的解密範圍

破解單字母替換加密法(理論基礎)

破解單字母替換加密法其實相當簡單,主要分為五個步驟:

  1. 找出密鑰中每個加密單詞的詞型(word pattern)。
  2. 根據每個加密單詞的詞型,找出所有可能的英文單詞作為候選解密結果。
  3. 為每個加密單詞建立一個字母對映表,利用該單詞的候選解密單詞進行對映。
  4. 將所有加密單詞的字母對映表進行交集運算,得到一個綜合的字母對映表。
  5. 從綜合的字母對映表中移除已經確定的字母。

密鑰中包含的加密單詞越多,能夠進行交集運算的字母對映表就越多。進行交集運算的對映表越多,每個加密字母對應的潛在解密字母數量就越少。這意味著,密鑰訊息越長,就越有可能成功破解並解密。

函式解析

hackSimpleSub(message) 函式

此函式負責破解簡單替換加密法。

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)

內容解密:

  1. 初始化一個空白的字母對映表 intersectedMap
  2. 將輸入的訊息轉換為大寫並移除非字母字元,然後分割成單詞列表 cipherwordList
  3. 遍歷每個加密單詞,根據其詞型找出候選解密單詞,並建立對應的字母對映表 newMap
  4. newMapintersectedMap 進行交集運算,更新 intersectedMap
  5. 傳回最終的 intersectedMap,並移除已經確定的字母。

decryptWithCipherletterMapping(ciphertext, letterMapping) 函式

此函式根據給定的字母對映表解密密鑰。

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)

內容解密:

  1. 初始化一個替換金鑰 key,長度與字母表相同,初始值為 ‘x’。
  2. 遍歷每個加密字母,如果其對應的解密字母唯一,則更新 key
  3. 如果解密字母不唯一,則在密鑰中將該加密字母替換為底線 ‘_’。
  4. key 連線成字串,並使用它解密密鑰。

使用互動式命令列介面探索破解函式

在瞭解上述函式的工作原理之前,讓我們先在互動式命令列介面中使用它們,觀察不同輸入下的輸出結果。

假設我們要破解的密鑰是:OLQIHXIRCKGNZ PLQRZKBZB MPBKSSIPLC

首先,我們取得一個空白的字母對映表:

>>> letterMapping1 = simpleSubHacker.getBlankCipherletterMapping()
>>> letterMapping1
{'A': [], 'C': [], 'B': [], 'E': [], 'D': [], 'G': [], 'F': [], 'I': [], 'H': [], 'K': [], 'J': [], 'M': [], 'L': [], 'O': [], 'N': [], 'Q': [], 'P': [], 'S': [], 'R': [], 'U': [], 'T': [], 'W': [], 'V': [], 'Y': [], 'X': [], 'Z': []}

接下來,我們計算第一個加密單詞 OLQIHXIRCKGNZ 的詞型:

>>> import makeWordPatterns
>>> wordPat = makeWordPatterns.getWordPattern('OLQIHXIRCKGNZ')
>>> wordPat
'0.1.2.3.4.5.3.6.7.8.9.10.11'

然後,我們查詢具有相同詞型的英文單詞:

>>> import wordPatterns
>>> candidates = wordPatterns.allPatterns['0.1.2.3.4.5.3.6.7.8.9.10.11']
>>> candidates
['UNCOMFORTABLE', 'UNCOMFORTABLY']

最後,我們根據候選單詞更新字母對映表:

>>> letterMapping1 = simpleSubHacker.addLettersToMapping(letterMapping1, 'OLQIHXIRCKGNZ', candidates[0])
>>> letterMapping1
{'A': [], 'C': ['T'], 'B': [], 'E': [], 'D': [], 'G': ['B'], 'F': [], 'I': ['O'], 'H': ['M'], 'K': ['A'], 'J': [], 'M': [], 'L': ['N'], 'O': ['U'], 'N': ['L'], 'Q': ['C'], 'P': [], 'S': [], 'R': ['R'], 'U': [], 'T': [], 'W': [], 'V': [], 'Y': [], 'X': ['F'], 'Z': ['E']}

從結果可以看出,OLQIHXIRCKGNZ 中的字母被對映到了 UNCOMFORTABLE 中的對應字母。這個過程可以幫助我們逐步破解密鑰。

簡單替換密碼破解技術深度解析

密碼破解流程分析

在簡單替換密碼的破解過程中,我們逐步建立並更新密碼字母對映表,以逐漸還原明文內容。以下將詳細解析整個破解流程中的關鍵步驟與技術細節。

建立初始密碼字母對映表

首先,我們需要為每個密鑰字建立一個空白的密碼字母對映表。以第一個密鑰詞 OLQIHXIRCKGNZ 為例,我們透過比對詞模式在字典中尋找可能的候選詞,獲得如 UNCOMFORTABLEUNCOMFORTABLY 等候選詞。

程式碼範例:建立初始對映表
letterMapping1 = simpleSubHacker.getBlankCipherletterMapping()
wordPat = makeWordPatterns.getWordPattern('OLQIHXIRCKGNZ')
candidates = wordPatterns.allPatterns[wordPat]
for candidate in candidates:
    letterMapping1 = simpleSubHacker.addLettersToMapping(letterMapping1, 'OLQIHXIRCKGNZ', candidate)

內容解密:

  1. getBlankCipherletterMapping() 用於建立一個空白的對映表,所有字母初始狀態均為空列表,表示尚未確定對應關係。
  2. makeWordPatterns.getWordPattern() 計算給定詞彙的模式,例如 OLQIHXIRCKGNZ 的模式與 UNCOMFORTABLE 相同。
  3. 透過 addLettersToMapping() 將候選詞的字母對映加入到 letterMapping1 中,若某字母已存在於列表中,則不會重複新增。

合併多個密鑰字的對映表

為了提高破解的準確性,我們需要對多個密鑰字進行相同的操作,並將它們的對映表進行交集運算,以獲得更精確的對映關係。

程式碼範例:合併對映表
letterMapping2 = simpleSubHacker.getBlankCipherletterMapping()
wordPat = makeWordPatterns.getWordPattern('PLQRZKBZB')
candidates = wordPatterns.allPatterns[wordPat]
for candidate in candidates:
    letterMapping2 = simpleSubHacker.addLettersToMapping(letterMapping2, 'PLQRZKBZB', candidate)

intersectedMapping = simpleSubHacker.intersectMappings(letterMapping1, letterMapping2)

內容解密:

  1. 對第二個密鑰詞 PLQRZKBZB 重複相同的流程,建立 letterMapping2
  2. 使用 intersectMappings() 將兩個對映表進行交集運算,獲得更精確的 intersectedMapping
  3. 交集運算確保了每個密鑰字母只對應到同時出現在兩個對映表中的明文字母。

逐步最佳化對映表

我們繼續對第三個密鑰詞 MPBKSSIPLC 進行相同的操作,並將結果與之前的 intersectedMapping 進行交集運算。

程式碼範例:進一步最佳化對映表
letterMapping3 = simpleSubHacker.getBlankCipherletterMapping()
wordPat = makeWordPatterns.getWordPattern('MPBKSSIPLC')
candidates = wordPatterns.allPatterns[wordPat]
for candidate in candidates:
    letterMapping3 = simpleSubHacker.addLettersToMapping(letterMapping3, 'MPBKSSIPLC', candidate)

intersectedMapping = simpleSubHacker.intersectMappings(intersectedMapping, letterMapping3)

內容解密:

  1. 對第三個密鑰詞建立 letterMapping3 並更新 intersectedMapping
  2. 每次交集運算都進一步縮小了可能的明文字母範圍,使最終的 intersectedMapping 更為精確。

解密與結果分析

最終,我們使用 decryptWithCipherletterMapping() 將得到的 intersectedMapping 應用於整個密鑰,獲得部分解密結果。

程式碼範例:應用對映表解密
decryptedText = simpleSubHacker.decryptWithCipherletterMapping('OLQIHXIRCKGNZ PLQRZKBZB MPBKSSIPLC', intersectedMapping)
print(decryptedText)

內容解密:

  1. 將最終的 intersectedMapping 用於解密整個密鑰。
  2. 由於部分字母仍有多個可能的對應,明文中有部分字母未能完全確定,以 _ 表示。