一、实验目的
1、通过实现简单的古典密码算法,理解密码学的相关概念
2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。
二、实验内容
1、题目内容描述
①随机生成加密密钥,并验证密钥的可行性
②从plain文件读入待加密明文
③进行仿射密码加密
④将加密后得到的密文放入cipher文件
⑤根据加密密钥计算解密密钥
⑥进行解密运算
⑦将解密后的文本放入plain_decrypted文件,与明文进行对比
2、关键代码的设计、实现与执行
设计思路:
先以时间产生随机数作为加密密钥a,b,但是这里要通过求最大公因数的方式来验证加密密钥a的可行性,即密钥a和N的最大公因数为1,即a,N互质(其中N为定义在Zn上的N):
下面代码是运用Euclid算法计算最大公因数的关键代码:
while(b!=0)//此处的b是N,也就是此实验的26,
{
temp=a%b;//得到的余数部分
a=b;//b做被模数
b=temp;//得到的余数做模数
}
return a;//求得的最大公因数
下面代码是随机生成密钥a,并通过调用最大公因数的函数验证密钥a可行性的关键代码部分:
while(gcd(a,N)!=1)//循环判断密钥a的可行性
{
srand(time(NULL));//以时间随机生成种子
a=rand()%N; //a随机生成,b也以同样的方式生成,但是不用判断可行性,只需进行一次随机生成
}
得到加密密钥后,从文件读入已经准备好的加密明文(也可通过记事本修改),下面代码是以读的方式打开文件,并判断是否在文件夹中存在命名的文件的关键代码部分:
FILE *plain_file = fopen("plain.txt", "r");//以读的方式打开
if (plain_file == NULL) {
printf("无法打开明文文件!\n");//判断无法打开给出提示
exit(1);//无法打开只能以异常终止结束运行
}
其中分析明文中的大小写字母,大写字母的ASCII码是从65到90,小写字母的ASCII码是从97到122,如实符号则不进行加密解密,直接返回,对字母利用放射加密y=ax+b mod q进行加密,下面代码是根据ASCII码将大小写代码转换,并进行加密的关键代码部分:
if(ch>='a'&&ch<='z')//ascii码转换,ch>=97&&ch<=122
{
ch-=97;
}
else if(ch>='A'&&ch<='Z')//ascii码转换,ch>=65&&ch<=90
{
ch-=65;
}
else
return ch;//如果是字符就直接跳过加密解密部分
return (ch*a+b)%N+97;//最后解密后的文件得到小写的字母
在得到加密后的密文后放入密文cipher文件,关键代码部分如下:
while ((ch = fgetc(plain_file)) != EOF) {
cipher_text=(char)encrypt(ch,a,b); //加密后直接将返回的密文放入文件中
fputc(cipher_text, cipher_file);
运用扩展Euclid算法求逆元a1,就可以得到解密密钥a模N的逆元a1,以及密钥c=b*a1
下面代码是运用扩展Euclid算法求a的逆元a1的关键代码部分:
while(b!=0) {//此处最开始的b也是N,就是此实验的26
int r = a%b;
int q = (a-r)/b;
int x2 = x0-x1*q;
int y2 = y0-y1*q;
a=b;
b=r;
x0=x1;
y0=y1;
x1=x2;
y1=y2;
}//当b==0时,判断是否a2>b2,若是,逆元应该是y0,若不是逆元应该是x0
最后对密文进行解密,步骤基本一样,只是密钥变成了a1和c。
最后将解密得到的文本放入plain_decrypted文件中,关键代码部分如下:
while ((ch = fgetc(cipher_file)) != EOF)
{
plain_text = (char)encrypt(ch,a1,c);
fputc(plain_text, plain_file);
}
实验结果截图:
- 实验结果分析
首先测试结果可见上图,可以看到对明文的加密结果以及对密文的解密结果,解密结果和明文是一致的,证明中间算法没有问题,然后在运行中也给了相关信息,如加密密钥和解密密钥以及文件命名等,且在明文中可以任意输入大小写字母,最后统一为小写字母,即最后return时加上97,结果如下:
当然也可以不统一为小写,或者统一为大写字母,只需要在加密encrypt函数处分类返回就行,也就是在小写字母处返回多加97,在大写字母处返回多加65,此处只是为了好看。
也可同一为大写字母如下,且有符号位:
对密文的解密也可以不统一为大写或小写,只需要各自return就行,得到结果如下:
特别的是关于符号以及空格等处理,并未对它们进行处理,而是相应的直接返回原始值,不对它们进行任何加密解密操作。
三、实验思考
1、实验过程总结
在刚开始进行编写程序的时候,由于基础理论知识不扎实,误将加密密钥直接代入解密式子中进行计算,一直出错,后来发现并及时解决了。特别是在符号及空位等的处理方式上,最开始是直接在代码中列出了常见符号的ASCII码,把它们从明文中单独提出来,然后不对它们进行加密解密操作,后来仔细看了实验视频,才发现直接用一个else就可以直接避免对所有的字符进行加密解密。
通过此次实验使我加深了对仿射密码的理解,也更清晰仿射密码加密解密每一步的原理,也加强了我自己的代码能力。
2、回答实验指导书最后提出的问题
①改进将仿射密码定义在Z29上,明密文空间除26个英文字母还包括空格、句号和引号。
这个方案与传统定义在Z26上的仿射密码相比有何优点?
②如果要实现此方案,你的程序应该如何进行调整?
①相对引用Z(26)的仿射密码Z(29)的仿射密码覆盖的范围更广,范围更广也就意味着有着更大的加密范围,意味着算法的安全性更高。
②首先要将宏定义的N改为29,然后加/解密过程对多出来的三个特殊符号进行单独的加/解密。