密码学课程设计之DES对称加密
前言
最近在进行密码学课程设计,感觉拿python进行设计会显得比较简洁易懂,本人python比较渣渣,所以就拿出来练一练。用零零碎碎的时间写了五六天才把主干部分写完,真是菜哭我自己了。在此还需要感谢一叶飘零学长的博客,遇到困惑时果然飘零学长的博客就是最好的去处。
正文
加密原理
**生成
1.输入8位字符**转换为64位二进制;
2.经过64位**经过PC-1置换为56位;
3.将56位分成两部分分别进行循环左移;
4.左移后组合,再进入PC-2置换为48位,第一轮**生成结束;
5.循环左移以后的两部分继续循环左移,再次进入PC-2置换,生成48位子**,如此进行16轮,存储16个48位的子**
加密处理
1.输入的字符先转换为二进制,每64位为一组进行加密;
2.每一组64位的二进制先进行IP置换打乱;
3.然后将64位分成2部分,分别放入Li和Ri
4.将Ri经过F函数处理,再与Li异或,然后将值赋给R(i+1)
5.将Ri赋值给L(i+1)
6.最后一轮,直接将Ri赋给R(i+1),再将Ri经过F函数处理,再与Li异或,然后将值赋给L(i+1)
7.将L和R合并后经过逆IP盒,得到密文C
F函数
1.先将输入的32位Ri进行扩展置换E,转换为48位
2.再与生成的子**进行异或
3.将异或后的值经过S盒压缩为32位
4.再进行P置换打乱顺序
加密详细过程
各个置换dict
dic_IP=[58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7]
dic_key1=[57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4]
dic_key2=[14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32]
dic_e=[32,1,2,3,4,5,4,5,6,7,8,9,8,9,10,11,12,13,12,13,14,15,16,17,16,17,18,19,20,21,20,21,22,23,24,25,24,25,26,27,28,29,28,29,30,31,32,1]
dic_s=[[14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13],[15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9],[10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12],[7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14],[2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3],[12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13],[4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12],[13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11]]
dic_p=[16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25]
dic_n=[1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
dic_IP2=[40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25]
初始IP置换
将初始明文字符转换为64位二进制,并进行第一次IP置换。
需要注意的就是在python中用format函数非常方便,没有bin函数开头的0b,在这里需要将转换的二进制字符补足8位(在左边补0即可)
def init_p(p):
s1=""
res=""
for i in range(8):
b='{:08b}'.format(ord(p[i]))
s1+=b
for i in range(64):
res+=s1[dic_IP[i]-1]
return res
字符串的循环左移
生成16个子**时需要循环左移函数,使用到的就是py的切片,先需要对**56位分为两部分,分别28位,然后这两部分分别循环左移
def zuoyi(n,ki):
temp=ki[n:len(ki)]
temp=temp+ki[0:n]
return temp
**初始编排-PC1
需要对输入的8个字符进行二进制转换并补足64位,然后将这64位字符进行PC-1置换,从64位置换为56位
def init_pc_1(k):
k1=""
k2=""
for i in range(8):
b='{:08b}'.format(ord(k[i]))
k1+=b
for i in range(56):
k2+=k1[dic_key1[i]-1]
return k2
**二次编排-PC2
将每次循环左移的**组合后进行PC-2置换,由56位转换为48位,并存储
def init_pc_2(k):
res=""
for i in range(48):
res+=k[dic_key2[i]-1] #将56位转换为48位
return res
按位异或
两个相同长度的字符进行按位异或
def xor(r_e,ki):
res=""
for i in range(len(r_e)):
xor_res=int(r_e[i],10)^int(ki[i],10)
if xor_res==1:
res+="1"
else:
res+="0"
return res
E盒扩展
将转换为二进制的64位明文分成两部分,Li和Ri,需要将Ri从32位二进制置换为48位,需要经过E盒扩展
def box_e(r):
temp_r=""
for i in range(48):
temp_r+=(r[dic_e[i]-1]) #将32位扩展为48位
return temp_r
S盒处理
这里有8个S盒,从E盒出来的48位数据分为8组,每组6位,分别进入8个S盒,上面将这8个S盒分别装入列表,并将这8个列表装入一个大列表,类似于C++里的二维数组。
将6位中的首位和末位组合并转换为10进制,为行
将6位中剩下的4位组合转换为10进制,为列
查找到对应行列的S盒中的值,将其转换为二进制字符并存储在res中
def box_s(r):
j=0
res=""
for i in range(0,len(r),6): #步长为6
begins=r[i:i+6]
hang=int(begins[0]+begins[5],2)
lie=int(begins[1:5],2)
num='{:04b}'.format(dic_s[j][16*hang+lie]) #将s盒中对应数字转为二进制字符串并补满4位
res+=num
j=j+1
return res
P盒置换
def box_p(r):
res=""
for i in range(32):
res+=r[dic_p[i]-1]
return res
生成16个子**
64位**先经过pc1置换为56位,分成两部分,分别循环左移,组合后进行二次**编排,将产生的16个子**按序存储在keylist中
def key_list(key):
temp_1=init_pc_1(key)
k_l=temp_1[0:28]
k_r=temp_1[28:56]
for i in range(16):
k_l=zuoyi(dic_n[i],k_l) #前28位左移n
k_r=zuoyi(dic_n[i],k_r) #后28位左移n
key_yiwei=k_l+k_r
key_res=init_pc_2(key_yiwei)
keylist.append(key_res)
F函数
先E盒置换,再与子**异或,再S盒处理,最后进行P盒处理
def fun_F(str1,key):
res_e=box_e(str1)
res_xor=xor(res_e,key)
res_s=box_s(res_xor)
res_p=box_p(res_s)
return res_p
IP逆置换
def IP_NI(c):
res=""
for i in range(64):
res+=c[dic_IP2[i]-1]
return res
加密一组
8个字符的加密
def DES_encode_one_group(p,k):
p_bin=init_p(p) #str 64
p_left=p_bin[0:32] #str 32 left
p_right=p_bin[32:] #str 32 right
key_list(k) #生成16个子**
for i in range(15): #16轮迭代
p_temp=p_right #暂存
fin_r=fun_F(p_right,keylist[i]) #Ri经过F函数
p_right=xor(fin_r,p_left) #Ri
p_left=p_temp #Li
final_right=p_right
final_left=xor(fun_F(p_right,keylist[15]),p_left)
fin=final_left+final_right
fin=IP_NI(fin)
return fin
加密全部
8个字符一组进行分组,每组分别进行加密
def DES_encode(p,k):
res=""
i=0
while p[i:i+8]!="":
res+=DES_encode_one_group(p[i:i+8],key)
i=i+8
return res
解密一组
一组64位二进制进行解密
def DES_decode_one_group(c):
cipher=""
#key_list(k)
for i in range(64):
cipher+=c[dic_IP[i]-1] #c经过IP1置换放入cipher
cipher_left=cipher[0:32]
cipher_right=cipher[32:]
i=15
while i>0:
cipher_temp=cipher_right
cipher_right=xor(fun_F(cipher_right,keylist[i]),cipher_left)
cipher_left=cipher_temp
i=i-1
fin_right=cipher_right
fin_left=xor(fun_F(cipher_right,keylist[0]),cipher_left)
final=fin_left+fin_right
final=IP_NI(final)
ming=""
for x in range(0,64,8):
ming+=chr(int(final[x:x+8],2))
return ming
解密全部
每组64位二进制,分组解密
def DES_decode(c):
i=0
res=""
while c[i:i+64]!="":
res+=DES_decode_one_group(c[i:i+64])
i=i+64
return res
运行结果及正确性分析
输入8个字符作为明文,8个字符作为**,得出密文为一串比特流,输入这串比特流,用上面生成的子**进行解密得到明文为先前输入的8个明文字符,验证了算法的正确性;
算法安全性分析
一重DES算法的**过短,只有56位有效**位,所以不具备很高的安全性,下面分析中间相遇攻击;
中间相遇攻击分析:
在 DES 的基础上,衍生了以下两种加密方式
1.双重 DES,使用两个**,长度为112比特,加密方程为C=Ek2(Ek1(P))
,但是双重DES是不可以抵抗中间相遇攻击的,构造如下,即分别枚举 K1 和 K2 分别对 P 进行加密和对 C 进行解密。
I=Ek1(P)
J=Dk2(C)
在我们对 P 进行加密完毕后,可以对加密结果进行排序,当我们对 C 进行解密时,可以每解密一个,就去对应的表中查询,看看是否相等,如此进行中间相遇攻击。计算量大概在2^56左右。和一重DES的复杂度相当;
2.三重DES,加密方式如下,3 个不同的**,k1,k2,k3 互相独立,一共 168 比特。可以抵御中间相遇攻击;
C=Ek3(Dk2(Ek1(P)))
P=Dk1(Ek2(Dk3(C)))
有效性和合理性分析
DES 现在已经不被视为一种安全的加密算法,拿DES来进行加密已经认为是不合理的,主要原因是它使用的56位**过短,为了提供实用所需的安全性,可以使用 DES 的派生算法 三重DES 来进行加密,现在的三重DES是具备安全性的,可以抵抗中间相遇攻击,比较合理有效;
算法编写过程中遇到的问题及解决
1.算法编写过程中,Feistel网络密码结构中的子**每一轮都需要更新;
2.一开始写十六轮迭代时,直接就是写16轮循环,每次得到的R作为下次加密的L,而上一轮得到的异或结果作为R,这样得到的结果是错误的,因为Feistel网络密码结构中第16轮迭代时,右部分直接就是第十五轮得到的R15,左部分是R15经过F函数后与L15异或得到的,并不是简单的16轮重复迭代;
3.一开始的解密过程错误,是因为我直接简单的拿加密的算法当解密的算法了,没有观察到,解密时的每一轮迭代中,子**是从第16个到第1个倒序添加的,这点格外重要;