crytography--Index of Coincidence(重合因子)

最近在看密碼學,講到用同一個密碼重複加密一段原文,能夠用重合因子來破解,上網查到的資料大部分是英文,因此準備把看到的資料http://en.wikipedia.org/wiki/Index_of_coincidence總結一下。ide

重合因子(index of coincidence)就是任意拿出兩個字母,兩個字母相同的機率。函數

以英文字母爲例,從26個字母中隨機拿出一個字母的機率是1/26,隨機選擇兩個字母,選擇出相同字母對的機率是測試

26x(1/26)x(1/26)=0.0385.加密

咱們知道,英文中字母的出現頻率是有規律的,好比e爲13%,a爲8%,c爲3%……因此,經過計算英文的重合因子可得:spa

(13/100)x(13/100)+(8/100)x(8/100)+(3/100)x (3/100)...=0.0667指針

對於一段文字,咱們能夠經過以下公式計算其重合因子
圖片描述
其中,fi表示某個字母在該段文字中出現的個數。
那麼,這又和密碼有什麼關係呢?
當咱們用簡單的替換密碼屢次替換原文獲得明文,這時候,原文中字母的統計屬性是被保留下來的。
破解過程:
(1)獲得密碼長度
1.Kasiski 測試法
2.對密碼長度keylen進行猜想,將全部密文分爲keylen*up(cipherlen/keylen)的二維矩陣(up(float a)是上取整函數),那麼對於每一列,都是用一個密鑰份量ki來加密的(對於這一列,至關於凱撒加密法),所以,該列加密後的密文保持加密前的統計屬性!那麼咱們對不一樣的keylen進行測試,從其中獲得和英文重合因子差異最小的那個keylen極可能就是密鑰長度。
(2)對每一列密文經過相關性尋找密碼份量ki,最後所有ki拼接成key=k1k2..kn.
獲得了密鑰長度,咱們就能夠對每一列求其密鑰份量ki,作法是窮舉全部a-z解密,計算解密後該列字母和英文的相關度,如何計算相關度呢,按照以下公式:
圖片描述
其中,ni表示解密後你獲得的第i個字母的頻率,fi爲對應英文字母在英文中的統計頻率。效果就是:
(解密後a的頻率 x a在英文中的頻率)+(解密後b的頻率 x b在英文中的頻率)+...(解密後z的頻率 x z在英文中的頻率),能夠證實該值最大的狀況時,對應的ki是正確的密鑰份量

附上測試代碼:調試

#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
#include<memory.h>
#define N 200
#define MAX_KEY_LEN (10+1)
char  cipher[N];//="qpwkalvrxcqzikgrbpfaeomfljmsdzvdhxcxjyebimtrqwnmeaizrvkcvkvlxneicfzpzczzhkmlvzvzizrrqwdkechosnyxxlspmykvqxjtdciomeexdqvsrxlrlkzhov";
int   a[26];
float english[26]={0.082,0.015,0.028,0.043,0.127,0.022,0.020,0.061,0.070,0.002,0.008,0.040,0.024,
                    0.067,0.075,0.019,0.001,0.060,0.063,0.091,0.028,0.010,0.002,0.001,0.002,0.001};
float target=0.067;
float saveIC[MAX_KEY_LEN];
#define ABS(x) ((x)>0?(x):-(x))//總括號必須有啊!
/*float ABS(float x){
    return x>0?x:-x;
}*/
float ic(char cipher[],int clen,int keylen){
    int   i,j,k,n=0;
    float sum=0;
    char  *p;
    for(i=0;i<keylen;i++){
        memset(a,0,sizeof(a));
        p=cipher+i;
        n=0;
        for(j=0;j<(clen/keylen+1);j++){
            if((p-cipher)>clen||*p == '\0') break;
            a[*p-'a']++;
            p+=keylen;
            n++;
        }
        for(k=0;k<26;k++){
            if(n==0||n==1) break;
            sum+=((float)a[k]*(a[k]-1)/(n*(n-1)));

        }
    }
    return sum/keylen;
}
int keylen(float saveIC[],float target){
    int i,len;
    float best=100.0;
    for(i=1;i<MAX_KEY_LEN;i++){
        float t=target-saveIC[i];
        if(best > ABS(t)){
            best=ABS(t);
            len=i;
        }
    }
    return len;
}

char key(char cipher[],int clen,int keylen,int column){
    int  i,j,k,n;
    int  temp[26];
    float cor,bestcor=0;
    char bestkey;
    char *p;

    for(i=0;i<26;i++){//進行a-z的窮舉
        memset(temp,0,sizeof(temp));
        n=0;
        cor=0.0;
        p=cipher+column;
        for(j=0;j<(clen/keylen+1);j++){//統計解密後的字母,保存在temp[]中
            if((p-cipher)>clen||*p=='\0') break;
            temp[(*p-('a'+i)+26)%26]++;
            n++;
            p+=keylen;
        }
        for(k=0;k<26;k++){//計算相關度
            cor+=(float)temp[k]/n * english[k];
        }
        /*printf("%d----",i);
        for(k=0;k<26;k++){
            printf("%d ",temp[k]);
        }
        printf("\n");
        */
        if(cor>bestcor){
            bestcor=cor;
            bestkey='a'+i;
        }
    }
    return bestkey;
}
void encryption(char plaintext[],int len,char key[],char cipher[]){//加密函數
    char* p=key;
    int i=0;
    while(i<len && plaintext[i]!='\0'){
        if(*p=='\0') p=key;
        cipher[i]='a'+(plaintext[i]-'a' + *p-'a')%26;
        i++;
        p++;
    }
}
void getkey(char cipher[],int clen,char k[]){
    int i,klen,col;
    memset(k,0,sizeof(k));
    for(i=1;i<MAX_KEY_LEN;i++){
        saveIC[i]=ic(cipher,clen,i);
    }
    klen=keylen(saveIC,target);
   // printf("len %d\n",klen);
   //---------------------------
    for(col=0;col<klen;col++){
        k[col]=key(cipher,clen,klen,col);
    }

}
void decryption(char cipher[],int clen,char key[],char plaintext[]){
    char* p=key;
    int i=0;
    while(i<clen && cipher[i]!='\0'){
        if(*p=='\0') p=key;
        plaintext[i]='a'+(cipher[i]-'a'-(*p-'a')+26)%26;
        //printf("%c",plaintext[i]);
        i++;
        p++;
    }
}
int main(){
    int i=0;
    char c,key1[MAX_KEY_LEN],key2[MAX_KEY_LEN];
    char mess[N],plaintext[N];
    memset(key1,0,sizeof(key1));
    memset(key2,0,sizeof(key2));
    memset(mess,0,sizeof(mess));
    memset(plaintext,0,sizeof(plaintext));
    memset(cipher,0,sizeof(cipher));

    printf("input your message:");
    scanf("%s",mess);
    printf("input your key:");
    scanf("%s",key1);
    encryption(mess,sizeof(mess),key1,cipher);
    printf("after encryption,cipher is :%s\n",cipher);
    getkey(cipher,sizeof(cipher),key2);
    printf("the key is:%s\n",key2);
    decryption(cipher,sizeof(cipher),key2,plaintext);
    printf("the message is:%s\n",plaintext);
    return 0;
}

測試發現:
(1)加密樣本必須儘量接近天然語言,並且加密文本長度必須達到必定的長度,才能知足統計規律。
(2)同時,若是密碼長度過大,那麼這種統計規律也不是很好(由於解密時每列字母變少,統計規律減弱)。
(3)知足足夠多的字母且密碼長度適宜時,統計規律較好,解密效果明顯。
侷限和不足:
(1)只能解決純小寫字母的加解密,不能處理帶有空格和標點的狀況,這是本程序的侷限性
(2)某些狀況下發生崩潰,不知什麼緣由,猜想是解密函數有BUG,還需調試。
收穫:
(1)利用重合因子和英文的統計規律,解密用同一段密鑰加密明文的加密方法。
(2)找資料仍是要找老外啊!
(3)英文水平太渣,須要提升。
(4)程序中發現,對於
函數void f(char str[])
{
int len=sizeof(str)//==4
}
對於聲明char str[N];
int len=sizeof(str);//==N
對於指針char *str;
int len=sizeof(str);//==4
寫帶參數宏的時候,不要吝惜括號的使用code

相關文章
相關標籤/搜索