流加密的密文解密

流加密的密文解密

解密目标

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

解密前提

  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 (程序说明)

传送门:下载

相关文章
相关标签/搜索