一、实验室名称:攻防实验室
二、实验项目名称:多表代换密码算法实现
三、实验学时:2 学时
四、实验原理:
五、实验目的:
1、熟悉多表代换密码算法;
2、掌握密码算法中参数选取、**生成、加密和解密的基本流程。
六、实验内容:
实现n=3的多表代换密码*,能够随机生成**对输入的英文字母信息进行加密或正确解密。
七、实验器材(设备、元器件):
学生每人一台PC,安装Windows 7操作系统及VC++/Python开发环境。
八、实验步骤:
1.**生成
(1)随机生成3ⅹ3可逆矩阵A,其中
计算其行列式并模去26,若其行列式等于零或与26不互素,则重新生成矩阵。矩阵生成后,计算其在模26下的逆矩阵。
//产生满足条件的随机三阶矩阵A
srand(time(0));
do{
for(i=0;i<M;i++){
for(j=0;j<M;j++){
A[i][j]=rand()%26; } }
det = -1;
for(i=1; det < 0; i++){
det = ((A[0][0]*A[1][1]*A[2][2]+A[1][0]*A[2][1]*A[0][2]+A[0][1]*A[1][2]*A[2][0]-A[0][2]*A[1][1]*A[2][0]-A[0][0]*A[1][2]*A[2][1]-A[0][1]*A[1][0]*A[2][2]) + 26 * i)%26; //行列式的表达式
}
}while(gcd(det,26)!=1||det==0); //行列式必须不等于0并且与26互素
printf(“Encryption matrix A: \n”);
for(i=0;i<3;i++){
for(j=0;j<3;j++){
printf("%d “,A[i][j]); }
printf(”\n");
} //输出生成的A矩阵
这里我和实验二中的Hill2密码一样,**的生成采用随机产生符合条件的加密矩阵的形式。随机产生三阶矩阵并模26,计算其行列式的值,三阶行列式的计算公式如图所示。
其中实线相连的三个元素前面的符号是正值,虚线相连的三个元素前面的符号是负值,将相连的符合相乘,再将各组乘得的结果相加并模26,就可以得到三阶行列式的值,若其行列式等于零或与26不互素,则重新生成矩阵。
//以下为求A的伴随矩阵
comA[0][0] = (A[1][1] * A[2][2] - A[2][1] * A[1][2]);
comA[1][0] = -(A[1][0] * A[2][2] - A[1][2] * A[2][0]);
comA[2][0] = (A[1][0] * A[2][1] - A[1][1] * A[2][0]);
comA[0][1] = -(A[0][1] * A[2][2] - A[0][2] * A[2][1]);
comA[1][1] = (A[0][0] * A[2][2] - A[0][2] * A[2][0]);
comA[2][1] = -(A[0][0] * A[2][1] - A[0][1] * A[2][0]);
comA[0][2] = (A[0][1] * A[1][2] - A[0][2] * A[1][1]);
comA[1][2] = -(A[0][0] * A[1][2] - A[0][2] A[1][0]);
comA[2][2] = (A[0][0] * A[1][1] - A[0][1] * A[1][0]);
在HILL2加密中,求二阶矩阵的伴随矩阵比较简单,对于三阶及以上矩阵的伴随矩阵,主对角元素是将原矩阵该元素所在行列去掉再求行列式。非主对角元素是原矩阵该元素的共轭位置的元素去掉所在行列求行列式乘以
,x和y为该元素的共轭位置的元素的行和列的序号,序号从1开始的。主对角元素实际上是非主对角元素的特殊情况,因为x=y,所以
,一直是正数,没必要考虑主对角元素的符号问题。
i = 1;
while(1){
if((det * i) % 26 == 1){
invdet = i;
break;}
else i++;
} //求三阶矩阵A的行列式的逆元为invdet
//以下求A的逆矩阵
for (i = 0; i < 3; i++){
for (j = 0; j < 3; j++){
invA[i][j]=invdetcomA[i][j];
invA[i][j] %= 26;
if (invA[i][j] < 0) invA[i][j] += 26;
}}
求A的逆使用公式,用A的行列式的逆元为invdet与伴随矩阵相乘。这些计算都是在mod 26的条件下进行。
(2)生成3维向量
,其中
//以下生成B矩阵
do{
for (i = 0; i < 3; i++)
B[i]=rand()%25;
}while(B[1]==0&&B[2]==0&&B[0]==0);
printf(“In this experiment,we use Key B as follows:\n”);
printf("%d %d %d\n",B[0],B[1],B[2]);
对于**B向量而言,其元素没有什么条件限制,保证其中的每个元素的范围在0-25范围内即可。
(3)保存A,A-1,B作为秘***。
2. 加密
(1)输入一段英文,将其转变成0~25之间的整数,并将这些整数分为3个一组;
下面就要把输入的英文字母转化为0-25之间的整数,由于输入的明文字符串有大小写之分,所以这里统一将大写字母转化为小写字母进行加密,a-z分别对应0-25。
for(i=0; i<len; i++){
if(pla[i] >= ‘A’ && pla[i] <= ‘Z’){
pla[i] = pla[i] + 32;
}
tran1[i] = pla[i] - ‘a’;
}
对于
(2),计算
这里需要注意数组类型的区分,tran1,s,ciph都是char型的数组,而T1和cip是int型的数组。通过算法得到s数组之后要注意加上97(‘a’)的,由0-25的数值通过加上‘a’得到对应的字母的ascll码值,从而ciph输出相应的密文字母。
以下是C语言代码实现:
for(i=0; i<len; i=i+3){
T1[0] = tran1[i];
T1[1] = tran1[i + 1];
T1[2] = tran1[i + 2];
// cip存储密文int值
cip[0] = ((A[0][0]*T1[0]+A[0][1]*T1[1]+A[0][2]*T1[2])+B[0]) % 26;
cip[1] = ((A[1][0]*T1[0]+A[1][1]*T1[1]+A[1][2]*T1[2])+B[1]) % 26;
cip[2]= ((A[2][0]*T1[0]+A[2][1]*T1[1]+A[2][2]*T1[2])+B[2]) % 26;
s[i] = cip[0];
s[i + 1] = cip[1];
s[i + 2] = cip[2];
}
//准备输出解密密文,将这些密文都放入到ciph数组中,此时ciph数组内的元素个数一定是3的倍数
for(i=0; i<len; i++)
ciph[i] = s[i] + ‘a’;
(3)循环加密直到所有的字母都加密完毕。(思考:若输入的英文包含的字母个数不是3的倍数,如何处理?)
在Hill2加密算法中,由于需要加密的明文是每两个字符成为一组进行加密的,所以我们需要考虑明文字符串中字符的的奇偶个问题。而对于多表代换算法,这些字符是每三个成为一组进行变换,所以我们需要考虑mod 3余0、1、2三种情况,并对于不足三个字符的组进行补齐,这里补上元素‘a’。
len=strlen(pla)-1; //使用这样方式读写数组最后一个会多出一个空格,我们在操作中需要丢弃这个空格,所以strlen(pla)-1才是实际的长度
if(len % 3 == 2){ //填充
pla[len] = ‘a’;
len = len+1;
flag = 1;
}
if(len % 3 == 1){ //填充
pla[len] = ‘a’;
len = len+1;
pla[len] = ‘a’;
len = len+1;
flag = 2;}
由于加密算法的设计,明文必须每三个一组进行加密,这一组整数要看作为一个整体,在得到密文后,明文与密文并不是一一对应的关系,而是每三个与密文的三个相对应。同一串明文,在末尾补的字母不同会导致得到的密文不同,这里需要注意,不可把明文与密文单个对应。
(4)保存密文。
为了方便后续的解密工作,在把密文根据需要输出的同时,我把原密文储存在output.txt文档中。
3. 解密
(1)读入密文,将其转变成0~25之间的整数,并将这些整数分为3个一组;
这里的密文字符个数一定是3的倍数,因为在加密中得到的密文一定是3的倍数并储存咋output.txt中,解密部分读入的密文就来自这个文本文档。所以解密时无需对密文字符串进行长度判断、添加哑字母,直接将其转变成0-25之间的整数,即可以进行解密。
len=strlen(ciph); //此时的len依旧是3的倍数
for(i=0; i<len; i++){
tran2[i] = ciph[i] - ‘a’;
}
(2)对于,计算
解密的过程就是加密的逆过程,只需将加密的步骤逆运算即可。
// 得到解密后的明文
for(i=0; i<len; i=i+3){
T2[0] = tran2[i];
T2[1] = tran2[i + 1];
T2[2] = tran2[i + 2];
x[0]=T2[0]-B[0]; //x矩阵计算C-B的值
x[1]=T2[1]-B[1];
x[2]=T2[2]-B[2];
// msg存储明文int值
msg[0] = (x[0]*invA[0][0]+x[1]*invA[0][1]+x[2]*invA[0][2]) % 26;
msg[1] = (x[0]*invA[1][0]+x[1]*invA[1][1]+x[2]*invA[1][2]) % 26;
msg[2] = (x[0]*invA[2][0]+x[1]*invA[2][1]+x[2]*invA[2][2]) % 26;
if(msg[0]<0) msg[0]+=26;
if(msg[1]<0) msg[1]+=26;
if(msg[2]<0) msg[2]+=26;
//mes矩阵接受解密后得到的明文的ascll码值
mes[i] = msg[0];
mes[i+1]=msg[1];
mes[i+2]=msg[2];
}
(3)循环解密直到所有的字母都解密完毕。
(4)保存明文并将其与开始加密的明文进行比较,判断是否正确。
九、实验数据及结果分析:
(1)实验参考代码:
请移步资源区
(2) 加密及解密实例结果截图:
(1)当明文字符个数为3的倍数余2时:
待加密的明文plain.txt:
生成随机矩阵进行加密并解密:
(2)当明文字符个数为3的倍数余1时:
待加密的明文plain.txt:
(3)当明文字符个数为3的倍数时:
待加密的明文plain.txt:
十、实验结论:
实现n=3的多表代换密码*,并且能够随机生成**对输入的英文字母信息(不区分大小写)进行加密或正确解密。
十一、总结及心得体会:
多表替代加密算法需要用到很多书论方面的知识。求模整数下的逆矩阵也很有技巧,需要注意每次运算都要模去26。
多表代换加密及解密算法与Hill2加密解密算法大体思路是一致的,区别在于,Hill2算法是将明文或密文串两两分为一组,使用二阶加密矩阵来进行的加密与解密运算,而多表代换则使用三阶加密矩阵,将明文与密文每三个划为一组,来进行加密与解密算法。多表代换相对Hill2的情况略复杂,因为面对模3余1、2和模3整除的三种情况,来添加哑字母。另外三阶矩阵和三阶行列式的运算也较为复杂。但是两种密码算法的思路大体一致,代码的实现也基本相同。只要理解了Hill2算法,多表代换算法也不难实现。
另外一个需要注意的地方是,得到加密后的密文时,不应该根据明文中哑字母位对应的密文字符舍掉,因为多表代换中,明文与密文是三三对应的,不能在同一组(3个)明文通过加密得到的一组(3个)密文的情况下,将其中的个别字符的明文与密文单独对应,它们不是一一对应关系。所以我加密得到的密文需要储存在output.txt中,在后续解密时直接从该文本文档中输入。
十二、对本实验过程及方法、手段的改进建议:
代码的实现可以写得更简洁明朗一些,逻辑上也应该更加全面。本次实验几乎与Hill2算法的思路和代码一致,其实可以有更多的思考,找到更合适更简洁的方法。