最近在看密碼學,講到用同一個密碼重複加密一段原文,能夠用重合因子來破解,上網查到的資料大部分是英文,因此準備把看到的資料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