维吉尼亚密码破解实验报告
思路
主要就是计算重合指数来进行破解
遍历尝试秘钥长度
在确定秘钥长度的基础上,对于每一个分组尝试计算偏移量——相当于破解单个的凯撒密码,用到的也是重合指数的计算
代码分析
在代码所在的文件夹中”密文.txt”,”明文.txt”分别存放要破解的密文和破解得到的明文,通过文件输入输出函数进行处理。用到向量将密文的字母全部读入。
int main()
{
char * name[3]={NULL,NULL,NULL};
name[0]="密文.txt";
name[1]="明文.txt";
if( !name[0] )
{
cout<<"cracker: too few parameters"<<endl;
//printUsage();
return 0;
}
vector<char> m;
//读入文章内容
readFile(name[0],m);
//破解秘钥
crack(name[0],"cracked.txt",1);
//破解密文
decrypt(name[0],name[1],key);
return 0;
}
然后先尝试不同的秘钥长度下,重合指数和统计学的重合指数的方差,遍历1~256不同的长度的可能秘钥长度,认为方差最小的秘钥长度就为实际的秘钥长度。
//mode==1的时候是计算重合指数,mode==0的时候是检验一定偏移的时候重合指数是否符合要求
double calcKasiski(vector<char> seq, int mode = 1)//mode 0:check mode
{
int freq[26]={
0};
double result=0;
int n = seq.size();
for(int i=0;i<n;i++)
{
if( (seq[i]-'a'<0) || (seq[i]-'a'>=26) )
{
cout<<"calcKasisiki():seq-\'a\' overflow"<<endl;
return 0;
}
freq[seq[i]-'a']++;
}
double Pi=0;
for(int j=0;j<26;j++)
{
if(mode==1)
{
Pi = freq[j]*1.0/n;
}
else
{
Pi = freq_table[j]/100;
}
result = result + Pi * (freq[j]-1)*1.0/(n-1);
}
return result;
}
// 秘钥长度分析
double klenAssess(const vector<char> raw_text,int klen)
{
vector<char> m[klen];
char c;
int n=raw_text.size();
for(int i=0;i<n;i++)
{
c=raw_text[i];
if(c==EOF)
break;
m[i%klen].push_back(c);
};
double sqdiff=0;
for (int j=0;j<klen;j++)
{
double ksk = calcKasiski(m[j],1);
sqdiff = sqdiff + (ksk-KASISKI)*(ksk-KASISKI);
}
sqdiff=sqdiff*1.0/klen;
cout<<"square diff of ksk for klen="<<klen<<": "<<sqdiff<<endl;
return sqdiff;
}
在确定秘钥长度的基础上,将密文以秘钥长度为步长分组,对于每一个分组,遍历26个可能的偏移量,此时也是用到重合指数计算,选择重合指数最小的偏移量作为该组的偏移量,并得到对应的秘钥的字母。
//计算每一组的偏移,选择检验模式的,计算重合指数最符合的偏移
char kCrackShift(const vector<char> list)//only a-z in list
{
double result[26]={
0};
for(int j=0;j<26;j+&