流加密的密文解密

流加密的密文解密

解密目標

給出十篇加密的樣例密文,求解密一篇特定的密文算法

解密前提

  1. 所有密文使用同一Key加密
  2. 加密的方法只是簡單的異或操做
  3. 原文絕大多數的內容都是以字母和空格爲主

解密過程分析

首先,這些加密方式都是異或加密,使用的是同一個Key。咱們從異或加密的一些方法進行入手,異或有如下的特徵。函數

公式一:
\[(A \otimes C) \otimes (B \otimes C) = A \otimes B\]
公式二:
\[(A \otimes B) \otimes A = B\]測試

從上述公式咱們能夠得出一些推論ui

\[(原文_1 \otimes 密鑰) \otimes (原文_2 \otimes 密鑰) = 原文_1 \otimes 原文_2 = 密文_1 \otimes 密文_2\]加密

\[(原文 \otimes 密鑰) \otimes 原文 = 密鑰 = 密文 \otimes 原文\]spa

從這個推論咱們能夠問題簡化爲,只須要知道原文和對應的密文,也就能知道了密鑰,最後再用密鑰異或待解密的密文,就能反向獲得其對應的原文了。3d

如今問題就變成了,如何知道密文對應的原文?code

因爲原文絕大多數的內容都是以字母和空格爲主,因此能夠利用空格ASCII碼異或的一個特色進行入手。先分析一下ASCII碼:blog

Char ASCII code Hex
Space 32 00100000
a~z 97~122 01100001~01111010
A~Z 65~90 01000001~01011010

所以,事件

\[Space \otimes alpha = 01XXXXXX\]

\[Alpha \otimes alpha = 00XXXXXX\]

這個推論代表了,在大部分狀況下,若是異或獲得的結果是字母,則雙方應該一方是空格,另外一方是對應字母的大小寫。(特殊狀況爲,標點符號的ASCII碼爲001XXXXX,效果等同於space,只是不能對應大小寫轉換,這就是此次實驗結果的干擾項,能夠看到有時候的確解出來了原文的字母,可是原文的字母明顯是錯的,不符合英文語法)

所以,找到空格就約等於破解了Key。

解密流程

說明

空格的機率:某一篇的特定一位和其他的各篇異或操做,若是結果是字母,則認爲它是空格的機率提升。因爲只須要比較大小,程序中沒有除以總的篇數,僅僅是比較分子大小。

閾值的選擇:在這裏我選擇的閾值是至少兩篇異或得出字母。

Key正確的機率:大量實驗結果代表,50%是最好的判決點!再次聲明,這個50%不是隨便選的,是通過了大量的實驗結果代表的,我也不知道它爲何就是50%,顯得我很不專業。

程序結構

封裝了一個解密類,用戶須要調用addCiphertext()方法添加樣例密文,添加越多,解密效果越好;而後調用decrypt()方法便可完成解密工做。getKey()方法和printKey()方法能夠查看Key,若是有待解密的密文,使用addTargetCiphertext()方法添加後調用decryptTarget()便可解密,解密效果根據樣例密文決定。

封裝了一個單詞拼寫類,但這不是主要實驗內容,使用了KMP搜索算法中的替換法則。

主要函數解析

void DecryptHelper::decryptKeyAtPosition(int position) {
    int maxCiphertext = ciphertextInt.size();
    int *spaceRatio = new int[maxCiphertext];
    int maxSpaceRatio = 0, maxSpaceRatioIndex = -1;
    // Statistics the space ratio for every ciphertext
    for (int i = 0; i < maxCiphertext; i++) {
        spaceRatio[i] = 0;
        if (position < ciphertextInt[i].size()) {
            int firstChar = ciphertextInt[i][position];
            for (int j = 0; j < ciphertextInt.size(); j++) {
                if (position < ciphertextInt[j].size() && j != i) {
                    int secondChar = ciphertextInt[j][position];
                    if (isalpha(firstChar ^ secondChar)) {
                        spaceRatio[i]++;
                        if (spaceRatio[i] >= 2 && spaceRatio[i] > maxSpaceRatio) {
                            maxSpaceRatio = spaceRatio[i];
                            maxSpaceRatioIndex = i;
                        }
                    }
                }
            }
        }
    }
    // Try to update the key
    while (maxSpaceRatioIndex != -1) {
        // Test if it is a real SPACE char
        int tryKey = ciphertextInt[maxSpaceRatioIndex][position] ^ SPACE;
        if (testKeyAtPosition(tryKey, position)) {
            key[position] = tryKey;
            break;
        }
        // Find another maxSpaceRatio key
        spaceRatio[maxSpaceRatioIndex] = 0;
        maxSpaceRatio = 0, maxSpaceRatioIndex = -1;
        for (int i = 0; i < maxCiphertext; i++) {
            if (spaceRatio[i] >= 2 && spaceRatio[i] > maxSpaceRatio) {
                maxSpaceRatio = spaceRatio[i];
                maxSpaceRatioIndex = i;
            }
        }
    }
    
    delete [] spaceRatio; 
}
bool DecryptHelper::testKeyAtPosition(int tryKey, int position) {
    int textCount = 0, alphaCount = 0;
    for (int i = 0; i < ciphertextInt.size(); i++) {
        if (position < ciphertextInt[i].size()) {
            textCount++;
            if (isalpha(ciphertextInt[i][position] ^ tryKey)) {
                alphaCount++;
            }
        }
    }
    if (alphaCount * 2 > textCount) {
        return true;
    }
    return false;
}

運行結果

Key的長度我聲明爲300,所以後面會有比較多的*是由於沒有這麼長的樣例密文來求解。破解率爲:26/31=0.8387=83.87%,儘管不盡如意,可是已經可以經過英文語法規則來獲知真正的內容。單詞拼寫檢查設置爲最多替換兩個字符,再多了也沒有意義,在80%+的破解率下運行良好。

一些心得

本次實驗是關於密碼破解的實驗。根據上文描述的破解原理,咱們能夠得出一些結論,使用同一個Key加密原文的,會使得密文更容易被破解。根據統計特徵,正常的英文文章中,字母的出現機率中會比非字母出現的機率多不少,所以,加密的密文越多,那麼就越符合統計學特徵,也就越容易被找到一個空格破解該位對應的Key。

下面來討論一下課件上的三個問題:

  • 許多空格問題
    這實際上是由算法決定的,若是算法的思路是僅僅找到三個字符,一個是空格兩個字母的話,可以很好的解決這一問題。可是,這個問題出現是小几率事件,通常狀況下的解密效果會變差,因此多空格程序會認爲找不到破解Key的關鍵,由於找到的決策是異或出現字母。

  • 沒有空格問題
    這個問題我以爲沒有什麼討論的意義,連空格都沒有,只能說解不出來好吧。哪怕剛恰好兩個字符異或獲得是字母,可是你會發現,最後解密的效果都是錯的[手動笑臉]。

  • 其餘字符問題
    上文的討論中我也已經提到了,標點符號的ASCII碼是001XXXXX,異或字母的結果仍是字母,因此可以在必定程度上影響解密效果。可是,符號的出現機率仍是相對比較小,並且通過了測試函數對這個可能的Key進行過濾處理,可以在必定程度上消除這個影響,從而更好地解密。

最後,此次實驗中學會了流加密的核心內容並獲得了實踐,我以爲流加密中也能夠作交換位置這種操做來增長加密的效果,或者是用一個特定的字符替換所有的空格再加密,可以大大地提升加密效果,也就更加不容易被破解(但這樣會大大提升咱們的實驗難度,我仍是匿了。

附錄

  • build
    • main.exe (可執行程序)
  • ciphertexts
    • multi-ciphertext.txt (樣例密文)
    • multi-targetCiphertext.txt (待解密密文)
  • result
    • final.txt (10篇待解密密文解密出來的原文)
  • model
    • small.txt (正確單詞的單詞庫)
  • DecryptHelper.h (解密類聲明)
  • DecryptHelper.cpp (解密類實現)
  • main.cpp (主函數)
  • Makefile (編譯文件)
  • README.md (程序說明)

傳送門:下載

相關文章
相關標籤/搜索