zoukankan      html  css  js  c++  java
  • 古典密码之维吉尼亚密码无密钥破解

    维吉尼亚密码 

    (又译维热纳尔密码)是使用一系列凯撒密码组成密码字母表的加密算法,属于多表密码的一种简单形式。

    维吉尼亚密文是通过明文加上密钥,根据维吉尼亚密码表来生成密文。

             维吉尼亚的密码强度是跟密钥的长度有关,或者你可以用几个密钥进行加密,如果几个密钥进行加密,那么我们尽量让不同密钥的长度互质,

    如果明文不长,当密钥长度和明文一样长,理论上是不可破译。

             然而,实际上很那做到密钥长度和明文长度一样长,因此这就给维吉尼亚密码破译提供了可能。

    1、首先,我们从维吉尼亚密码加密方式可以发现,假如密钥的长度为 k ,那么明文中第 Xi ,Xi+k ,Xi+2*k,……是不是用同一个密码加密,那么不就是凯撒密码吗? 那么问题来了,我们怎么获取到密钥长度是多少呢?

    2、Kasiski 实验

       为了获取到密钥长度,我们需要进行Kasiski 实验,什么是Kasiski 实验呢?

    假如有一段密文: ACEBTSSTRCESQSSTQRCK

    那么我们从中挑选出至少三个字符以上相同的字串,并且比较他们相邻两个字串相邻位置之差:比如密文中 "SST",它们相差了8。

    因为在加密过程中,出现至少三个字符以上相同的字串,明文不同的概率是很小的,那么我们是不是可以知道,密钥的长度一定是8的因子。

    如果还能找到其他至少三个字符以上相同的字串,那么密钥长度是它们的最大公约数的概率非常大。

    到目前为止我们可能已经有好几种密钥长度的可能了,那么怎么来确定密钥长度呢?或者说怎么求出密钥,获取明文呢?

    3、重合指数攻击

          设一门语言由n个字母组成,每个字母出现的概率为 Pi 则重合指数是指两个元素随机相同的概率之和,记作 CI =∑ Pi2  (1<= i <= n);

    经分析,英文中,一段文字是随机的话,CI =0.0385 ;如果这段文字是有意义的,那么 CI=0.065  (约等)。实际上计算的CI应该用这个公式

               L:密文长度; fi :在密文中的出现次数。  (公式来源

     

      这个有什么好处呢?

      好处就是用我们猜测的密钥长度来进行分组,分别计算每组的CI,再求个平均,计算当前密钥长度下,CI 的值与0.065相差多少。然后按照最接近0.065的密钥长度进行排序,为了提高解密的成功率,一般会取前5~10个较为接近的密钥长度作为猜测。

      

    4、字母频率分析

      密钥长度知道了,然而怎么获取密钥到底是多少呢?

           还是根据统计学:我们可以知道每个字母在英文中的频率

      字母表频率

      正常的文本中,每个字母出现的频率是遵循上述规则。

      那么破解密钥就变得很简单了,我们统计在某个密钥长度下的密各个组的字母频率,这样对单个组来说,就是凯撒密码,我们循环26次,判断哪种情况下字母频率与统计字母频率的内积最大,即 R=∑Pi*Qi  ('a'<= i <='z')  。

      这样我们对密钥的某个单个字符破解出来了,同理我们可以破解出密钥。

    最后从你程序输出的几组结果进行人工判别一下,哪个是有意义的明文。

    (转载请注明出处Thanks♪(・ω・)ノ)

    C++源程序:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 struct Node{
      4     float value; //重合指数差,与我们的标准重合指数的差值越小越好
      5     int length;
      6 };
      7 vector< Node > key; //存放key可能的长度和重合指数差
      8 set< int > key_len; //存放key可能的长度
      9 /*
     10 英文字母使用频率表 g
     11 */
     12 double g[]={0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153, 0.00772, 0.04025,0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150,0.01974, 0.00074};
     13 bool Greater_sort(Node a,Node b){
     14     return a.value<b.value;
     15 }
     16 /*
     17 Coincidence_index,计算所选分组的重合指数
     18 start表示分组的起点,length表示步长
     19 重合指数CI的实际估计值是
     20 X(i)=F(i)*(F(i)-1)/sum*(sum-1)
     21 ('a'<=i<='z',F(i)为i字符在当前分组出现的次数)
     22 对上述X(i)求和就是整个分组的重合指数CI
     23 */
     24 float Coincidence_index(string cipher,int start,int length){
     25     float index=0.000;
     26     int sum=0;
     27     int num[26];
     28     memset(num,0,sizeof(num));
     29     while(start<=cipher.length()){
     30         num[cipher[start]-'a']++;
     31         start+=length;
     32         sum++;
     33     }
     34     for(int i=0;i<26;i++){
     35         if(num[i]<=1) continue;
     36         index+=(float)(num[i]*(num[i]-1))/(float)((sum)*(sum-1));
     37     }
     38     return index;
     39 }
     40 /*
     41 Find_same()函数即是根据 kasiski测试法的原理
     42 我们可以获取key可能的长度
     43 */
     44 void Find_same(string cipher){
     45     for(int i=3;i<5;i++){
     46         for(int j=0;j<cipher.length()-i;j++){
     47             string p=cipher.substr(j,i);
     48             for(int k=j+i;k<cipher.length()-i;k++){
     49                 string tmp=cipher.substr(k,i);
     50                 if(tmp==p){
     51                     Node x;
     52                     x.length=k-j;
     53                     key.push_back(x);
     54                 }
     55             }
     56         }
     57     }
     58 }
     59 int gcd(int a,int b){
     60     if(b==0) return a;
     61     else return gcd(b,a%b);
     62 }
     63 /*
     64 
     65 求出可能的key的值的最大公因子
     66 经过重合指数检验,对key的长度进行排序
     67 
     68 */
     69 void Get_key(string cipher){
     70     Find_same(cipher);
     71     for(int i=0;i<key.size();i++){
     72         int x=key[i].length;
     73         for(int j=0;j<key.size();j++){
     74             if(key[i].length>key[j].length)
     75                 key_len.insert(gcd(key[i].length,key[j].length));
     76             else
     77                 key_len.insert(gcd(key[j].length,key[i].length));
     78         }
     79     }
     80     key.clear();
     81     set< int >::iterator it=key_len.begin();
     82     while(it!=key_len.end()){
     83         int length=*it;
     84         if(length==1){
     85             it++;
     86             continue;
     87         }
     88         float sum=0.000;
     89         cout<<length<<" ";
     90         for(int i=0;i<length;i++){
     91             cout<<Coincidence_index(cipher,i,length)<<"  ";
     92             sum+=Coincidence_index(cipher,i,length);
     93         }
     94         cout<<endl;
     95         Node x;
     96         x.length=length;
     97         x.value=(float)fabsf(0.065-(float)(sum/(float)length));
     98         if(x.value<=0.1)
     99             key.push_back(x);
    100         it++;
    101     }
    102     sort(key.begin(),key.end(),Greater_sort);
    103 }
    104 /*
    105 
    106 为了提高解密的成功率,我们取前面10个公因子进行求解
    107 对每个公因子的每个分子进行字母的拟重合指数分析
    108 由Chi测试(卡方检验),获取峰值点
    109 该峰值点极有可能是明文
    110 
    111 */
    112 void Get_ans(string cipher){
    113     int lss=0;
    114     while(lss<key.size()&&lss<10){
    115         Node x=key[lss];
    116         int ans[cipher.length()];
    117         memset(ans,0,sizeof(ans));
    118         map< char ,int > mp;
    119         for(int i=0;i<x.length;i++){
    120             double max_pg=0.000;
    121             for(int k=0;k<26;k++){
    122                 mp.clear();
    123                 double pg=0.000;
    124                 int sum=0;
    125                 for(int j=i;j<cipher.length();j+=x.length){
    126                     char c=(char)((cipher[j]-'a'+k)%26+'a');
    127                     mp[c]++;
    128                     sum++;
    129                 }
    130                 for(char j='a';j<='z';j++){
    131                     pg+=((double)mp[j]/(double)sum)*g[j-'a'];
    132                 }
    133                 if(pg>max_pg){
    134                     ans[i]=k;
    135                     max_pg=pg;
    136                 }
    137             }
    138         }
    139         cout<<endl<<"key_length: "<<x.length<<endl<<"key is: ";
    140         for(int i=0;i<x.length;i++){
    141             cout<<(char)((26-ans[i])%26+'a')<<" ";
    142         }
    143         cout<<endl<<"Clear text:"<<endl;
    144         for(int i=0;i<cipher.length();i++){
    145             cout<<(char)((cipher[i]-'a'+ans[i%x.length])%26+'a');
    146         }
    147         cout<<endl;
    148         lss++;
    149     }
    150 }
    151 int main(){
    152     string cipher;
    153     cin>>cipher;
    154     transform(cipher.begin(), cipher.end(), cipher.begin(),::tolower);
    155     Get_key(cipher);
    156     for(int i=0;i<key.size();i++){
    157         cout<<key[i].length<<"  and "<<key[i].value<<endl;
    158     }
    159     Get_ans(cipher);
    160     return 0;
    161 }
  • 相关阅读:
    【宁夏区域赛】G.Pot!
    【C#】上机实验二
    【C#】上机实验三
    Luogu P1437 敲砖块
    Luogu P1463 反素数
    Luogu P1445 樱花
    GHOJ 926 小X的AK计划
    【题解】Beads
    【题解】Antisymmetry
    【题解】A Horrible Poem
  • 原文地址:https://www.cnblogs.com/ISGuXing/p/9665904.html
Copyright © 2011-2022 走看看