(一)实验要求
1、编写程序实现分组密码(DES)加、解密:
(1)编程实现基于自己的名字来构造一分组密码的密钥;
(2)应用(1)获得的密钥将一段英文(或文件)进行加、解密;
2、用分组密码实现口令的安全:
如:登陆计算机(或连接服务器)时,用户的口令作为分组密码密钥,加密某个固定的明文,生成的密文存储在计算机中。下次登陆时,把生成的密文和已存储的密文进行比较,若一致则登陆成功。也可用其它方法,只要实现口令登陆即可。
(二)实验原理:
1.所需参数
key:8个字节共64位的工作密钥
data:8个字节共64位的需要被加密或被解密的数据
mode:DES工作方式,加密或者解密
2.初始置换
DES算法使用64位的密钥key将64位的明文输入块变为64位的密文输出块,并把输出块分为L0、R0两部分,每部分均为32位。初始置换规则为:将输入的64位明文的第1位置换到第40位,第2位置换到第8位,第3位置换到第48位。以此类推,最后一位是原来的第7位。
3.加密处理–迭代过程
经过初始置换后,进行16轮完全相同的运算,在运算过程中数据与密钥结合、函数f的输出经过一个异或运算,和左半部分结合形成新的右半部分,原来的右半部分成为新的左半部分。每轮迭代的过程可以表示如下:Ln = R(n - 1);Rn = L(n - 1)⊕f(Rn-1,kn-1)
函数f由四步运算构成:密钥置换(子密钥Kn的生成,n=0~16);扩展置换;S-盒代替;P-盒置换。
4.逆置换
将初始置换进行16次的迭代,即进行16层的加密变换,这个运算过程我们暂时称为函数f。得到L16和R16,将此作为输入块,进行逆置换得到最终的密文输出块。
5.DES解密
加密和解密可以使用相同的算法。加密和解密唯一不同的是密钥的次序是相反的。就是说如果每一轮的加密密钥分别是K1、K2、K3…K16,那么解密密钥就是K16、K15、K14…K1。为每一轮产生密钥的算法也是循环的。加密是密钥循环左移,解密是密钥循环右移。
(三)主要代码:
1. 类型转换功能:完成字符和二进制之间的相互转换
(1)转换为二进制(用于明文、密钥)
void change(char s[],int flag)///转换为二进制
{
int l=0;
for(int i=0; i<8; i++)
{
int n=s[i],time=0;
memset(zhongbit,0,sizeof(zhongbit));
while(n!=0)
{
zhongbit[time++]=n%2;
n/=2;
}
if(flag==1)
{
for(int j=7; j>=0; j--)
smbit[l++]=zhongbit[j];
}
else if(flag==2)
{
for(int j=7; j>=0; j--)
miyaobit[l++]=zhongbit[j];
}
}
}
(2)将二进制转换成16进制密文
void zhuan()///将二进制转换成16进制密文
{
int ans=0,l=0;
for(int i=0; i<64; i++)
{
if((i+1)%4==0)
{
ans=smbit[i-3]*8+smbit[i-2]*4+smbit[i-1]*2+smbit[i];
if(ans>9)
miwen[l++]='A'+ans-10;
else
miwen[l++]=ans+'0';
ans=0;
}
}
}
2.IP初始置换
void ipzhihuan()///第一次循环使用的ip置换
{
for(int i=0; i<64; i++)
mip[i]=smbit[IP[i]-1];///下标从0开始
fen();
memset(smbit,0,sizeof(smbit));
}
3. E盒扩展
void EK()///E盒扩展
{
memset(rr,0,sizeof(rr));
for(int i=0; i<48; i++)
rr[i]=Right[E[i]-1];///下标从0开始
}
4. 异或及S盒压缩
void SY(int k)///异或及S盒压缩
{
for(int i=0; i<48; i++)
rr[i]^=zimi[k][i];
int c=0;
for(int i=0; i<48; i++)
{
if((i+1)%6==0)///进入下个s盒
{
int w=(i+1)/6,h,l;///哪个盒,哪一行那一列
h=rr[i-5]*2+rr[i]*1;
l=rr[i-4]*8+rr[i-3]*4+rr[i-2]*2+rr[i-1]*1;
int ans=S[w-1][h][l];
int tt=0;
memset(er,0,sizeof(er));
while(ans)
{
er[tt++]=ans%2;
ans/=2;
}
for(int j=3; j>=0; j--)
SYa[c++]=er[j];
}
}
}
5. P盒置换
void PHe()///P盒置换
{
for(int i=0; i<32; i++)
Right[i]=Left[i]^SYa[P[i]-1];
for(int i=0; i<32; i++)
Left[i]=mip[32+i];
for(int i=0; i<32; i++)
{
mip[i]=Left[i];
mip[32+i]=Right[i];
}
fen();///分为左右两部分
}
6.逆置换
void nizhihuan()///逆初始置换表
{
for(int i=0; i<64; i++)
smbit[i]=mip[IP_1[i]-1];
}
void PC_1zhi()///置换选择pc1盒及循环移位
{
if(times==0)///只有当是第一次的时候才会进行PC置换盒1
{
for(int i=0; i<56; i++)
miyaopc1[i]=miyaobit[PC_1[i]-1];///下标从0开始
}
for(int i=0; i<28; i++) ///分为两部分,循环移位
{
int ans=i+xz[times];
if(ans>27)
ans-=28;
c[i]=miyaopc1[ans];///得到前半部分
d[i]=miyaopc1[28+ans];///得到后半部分
}
for(int i=0; i<28; i++)
{
miyaopc1[i]=c[i];
miyaopc1[28+i]=d[i];
}
times++;
}
7.轮置换,子密钥生成
void PC_2zhi()///16轮置换,pc2盒得到子密钥
{
for(int k=0; k<16; k++)
{
PC_1zhi();
for(int i=0; i<48; i++)
zimi[k][i]=miyaopc1[PC_2[i]-1];///将每一次产生的子密钥存下来
}
}
8.DES加密
void jiami() ///加密算法
{
PC_2zhi(); ///子密钥产生
ipzhihuan(); ///ip置换
for(int i=0; i<16; i++) ///一样的方式一共循环15次
{
EK(); ///E盒扩展
SY(i); ///S盒压缩
PHe(); ///p盒置换
}
for(int i=0; i<32; i++)
{
mip[32+i]=LL[16][i];
mip[i]=RR[16][i];
}
nizhihuan(); ///逆初始置换表
zhuan(); ///将二进制转换成16进制密文
}
9.DES解密
void jiemi() ///解密算法
{
cnt=0;
memset(LL,0,sizeof(LL));
memset(RR,0,sizeof(RR));
ipzhihuan(); ///ip置换
for(int i=0; i<16; i++) ///一样的方式一共循环15次
{
EK(); ///E盒扩展
SY(15-i); ///S盒压缩
PHe(); ///p盒置换
}
for(int i=0; i<32; i++)
{
mip[32+i]=LL[16][i];
mip[i]=RR[16][i];
}
nizhihuan(); ///逆初始置换表
}
10.主函数
int main()
{
init();
printf("请输入8位明文:");
scanf("%s",mingwen); //输入明文
change(mingwen,1); ///明文转换为二进制
printf("\n请输入8位密钥:");
scanf("%s",miyao); //输入密文
change(miyao,2); ///密钥转换为二进制
jiami(); ///加密算法
printf("加密完成,得到十六进制密文:\n");
printf("%s\n",miwen);
jiemi(); ///解密算法
printf("\n解密完成,得到明文:\n\n");
for(int i=0;i<64;i++)
{
if((i+1)%8==0)
{
int ans=smbit[i-7]*128+smbit[i-6]*64+smbit[i-5]*32+smbit[i-4]*16
+smbit[i-3]*8+smbit[i-2]*4+smbit[i-1]*2+smbit[i];
printf("%c",ans);
}
}
printf("\n");
return 0;
}
11. 口令安全(从网络上查找到了以下代码,但未能完成操作)
11. 口令安全(从网络上查找到了以下代码,但未能完成操作)
void koulinganquan()//口令安全验证
{
char key[17];
char fileName1[64];
char fileName2[64];
char fileP[Max];
char c[Max];
strcpy(fileName1,"m.txt");
strcpy(fileName2,"c.txt");
int plen;
int klen;
readStrFromFile(fileName2,c);
if(strlen(c) == 0)//无密码
{
printf("无密码,请输入密码\n");
printf("输入密码\n");
scanf("%s",key);
klen=strlen(key);//输入密码
while(1)
{
if(klen != 0&&klen%16 == 0)
{break;}
else
{
klen++;
strcat(key,"0");
}
}
readStrFromFile(fileName1,fileP);//读取密文文件
readPlainText(fileP, &plen);//读取明文文件
aes(fileP, plen, key);//开始加密
writeStrToFile(fileP,plen,fileName2);//写入密文文件
printf("输入成功!\n");
}
else
{
printf("输入密码\n");
scanf("%s",key);
klen=strlen(key);
while(1)//密钥填充
{
if(klen != 0&&klen%16 == 0)
{break;}
else
{
klen++;
strcat(key,"0");
}
}
readStrFromFile(fileName1,fileP);//读取密文文件
readPlainText(fileP, &plen);//读取明文文件
aes(fileP, plen, key);//开始加密
if(strcmp(fileP,c) == 0)//比较
{
printf("密码正确\n");
printf("若要修改密码请输入'1',否则输入'0'\n");
int i;
scanf("%d",&i);
if(i == 1)
{
printf("输入密码\n");
scanf("%s",key);
klen=strlen(key);
while(1)
{
if(klen != 0&&klen%16 == 0)
{break;}
else
{
klen++;
strcat(key,"0");
}
}
readStrFromFile(fileName1,fileP);
readPlainText(fileP, &plen);
aes(fileP, plen, key);//开始加密
writeStrToFile(fileP,plen,fileName2);
printf("修改成功!\n");
}
}
else
printf("密码错误\n");
}
}
实验结果
1.(1)DES加密:根据提示输入密钥和需要加密的明文,随后得到加密后的密文。
图1-1 DES加密
(2)DES解密:解密(1)中密文得到解密后的明文,发现与(1)中的明文相同,证明DES加解密的正确性。
图1-2 DES解密
2.分组密码实现口令的安全
登陆计算机(或连接服务器)时,用户的口令作为分组密码密钥,加密某个固定的明文,生成的密文存储在计算机中。下次登陆时,把生成的密文和已存储的密文进行比较,若一致则登陆成功。从网络上查找到了口令安全代码(见(二)中代码部分),多次实践,但因能力不足,最终未能完成页面的实现。
总结
1.进一步熟悉掌握了DES加密解密的原理
2.DES算法的特点:
(1)分组加密算法:以64位为分组。64位明文输入,64位密文输出。
(2)对称算法:加密和解密使用同一密钥
(3)有效密钥长度:为56位密钥通常表示为64位数,但每个第8位用作奇偶校验,可以忽略。
(4)代替和置换:DES算法是两种加密技术的组合:混乱和扩散。先替代后置换。
(5)易于实现:DES算法只是使用了标准的算术和逻辑运算,其作用的数最多也只有64 位
3.了解到DES要求密钥长度为8字节,那对于不是8字节的密钥的一些解决方法,例如:
若用户输入字符数小于8,可以在密钥最后随机填充足够的字符以补足8字节长度;
若用户输入字符数大于8,可以在这个字符串中随机选择8个字符作为密钥。
4.
对于分组密码实现口令的安全这部分实验内容,虽然最终未能实现其功能,但在做尝试的过程中也学习到了很多:上个学期曾做过一个数据库的登录实验(预先注册账号及密码,登录时直接输入用户密码登录),当时以为登录时只是比较输入的用户名、密码是否在已注册的数据库中即可,通过这次试验了解到,登陆计算机或连接服务器时,用户的口令是作为分组密码密钥,加密某个固定的明文,生成的密文存储在计算机中。下次登陆时,把生成的密文和已存储的密文进行比较,若一致才能够登陆成功。