DES介绍和分析:
DES算法将明文分成64位大小的众多数据块,即分组长度为64位。同时用56位密钥对64位明文信息加密,最终形成64位的密文。如果明文长度不足64位,则将其扩展为64位(如补零等方法)。
具体加密过程首先是将输入的数据进行初始换位(IP),即将明文M中数据的排列顺序按一定的规则重新排列,生成新的数据序列,以打乱原来的次序。然后将变换后的数据平分成左右两部分,左边记为L0,右边记为R0,然后对R0实行在子密钥(由加密密钥产生)控制下的变换f,结果记为f(R0,K1),再与L0做逐位异或运算,其结果记为R1,R0则作为下一轮的L1。如此循环16轮,最后得到L16、R16,再对L16、R16实行逆初始置换IP-1,即可得到加密数据。解密过程与此类似,不同之处仅在于子密钥的使用顺序正好相反。
DES全部16轮的加密过程如图2.1所示。
DES的加密算法包括3个基本函数。
1.初始换位(IP)
它的作用是把输入的64位数据块的排列顺序打乱,每位数据按照下面换位规则重新组合,即将第58位换到第1位,第50位换到第2位,……,依次类推。重组后的64位输出分为L0、R0(左、右)两部分,每部分分别为32位。
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
R0和K1经过f(R0,K1)变换后的输出结果,再和L0进行异或运算,输出结果做为R1,R0则赋给L1。L1和Rl同样再做类似运算生成L2和R2,……,经过16次运算后生成L16和R16。
2.f函数
f函数是多个置换函数和替代函数的组合函数,它将32位比特的输入变换为32位的输出,如图2.2所示。Ri经过扩展运算E变换后扩展为48位的E(Ri),与Ki+1进行异或运算后输出的结果分成8组,每组6比特的并联B,B=BlB2B3B4B5B6B7B8,再经过8个S盒的选择压缩运算转换为4位,8个4位合并为32位后再经过P变换输出为32位的f(Ri,Ki+1)。其中,扩展运算E与置换P主要作用是增加算法的扩散效果。
3.逆初始置换函数IP-1
它将L16和R16作为输入,进行逆初始换位得到密文输出。逆初始换位是初始换位的逆运算,换位规则如下所列:
子密钥生成:
DES的加密算法中除了上面介绍的3个基本函数,还有一个非常重要的功能模块,即子密钥的生成模块,具体子密钥的产生流程如图2.3所示。
输入的初始密钥值为64位,但DES算法规定,其中第8、16、……、64位是奇偶校验位,不参与DES运算。所以,实际可用位数只有56位,经过缩小选择换位表1(表2-2)即密钥置换PC-1的变换后,初始密钥的位数由64位变成了56位,将其平分为两部分C0、D0,然后分别进行第1次循环左移,得到Cl、Dl,将Cl(28位)、Dl(28位)合并后得到56位的输出结果,再经过缩小选择换位表2(表2-3)即密钥置换PC-2,从而得到了密钥K1(48位)。依次类推,便可得到K2、……、K16。需要注意的是,16次循环左移对应的左移位数要依据表2-1的规则进行。表2-1 左移位数规则
表2-2缩小选择换位表1
表2-3缩小选择换位表
设计一个程序可以利用DES算法进行加密和解密。
验证:假设明文为:123456ABCD132536,密钥为:AABB09182736CCDD
密文为:C0B7A8D05F3A829C
加密验证过程数据如下图:
实验代码为:
# -*- coding: UTF-8 -*-
import time
"""
DES算法实现加解密
@author WQ
@time 2020/12/2
"""
class DES():
"""
DES:加解密模块
"""
def __init__(self):
pass
def extract_keystream(self,model,keys):
"""
args{
model:模式1密钥为十六进制形式,模式2密钥为字符串形式
}
"""
self.original_keystream=''
self.keys=keys
if model==1:
for c in self.keys:
if c >= '0' and c <= '9':
c=ord(c)-48
self.original_keystream+= bin(c).replace('0b','').zfill(4)#单个字符不足8位补0
elif c >='A' and c <= 'F':
c=ord(c)-55
self.original_keystream+= bin(c).replace('0b','').zfill(4)#单个字符不足8位补0
elif model==2:
for c in self.keys:
self.original_keystream+= bin(ord(c)).replace('0b','').zfill(8)
else:
pass
if len(self.original_keystream)>64:
self.original_keystream=self.original_keystream[:64]
for i in range(64-len(self.original_keystream)):#秘钥不足64位,用0补全
self.original_keystream+='0'
"""
#64位密钥剔除奇偶校验位得到56位密钥
newkeystream=''
i=1
for k in keystream:
if(i%8!=0):
newkeystream=newkeystream+k
i=i+1
self.keystream=newkeystream #剔除后的原始56位密钥
"""
def reverse_function1(self):
"""
args{
密钥置换1
self.keystream:56位原始密钥
}
"""#置换选择表1
PC_1=[[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]]
self.reverse1=[]
#print(len(self.original_keystream))
for i in PC_1:
for j in i:
self.reverse1.append(self.original_keystream[j-1])
return self.reverse1
def reverse_function2(self):
"""
args{
密钥置换2
}
"""#置换选择表2
PC_2=[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]
self.reverse2=self.reverse2_0x=[]#置换2后的的子秘钥列表
cyc_keystream=[]#移位后的子秘钥列表
left=self.reverse1[:28]#置换1后左半部
right=self.reverse1[28:]#置换1后右半部
cyc_List=[1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]#移位数列表
for i in cyc_List:
left=self.cycleLeft(left,i)
right=self.cycleLeft(right,i)
cyc_keystream.append(left+right)#移位后的子秘钥列表
"""
for k in range(0,28):
left[k]=newleft[k]
right[k]=newright[k]
"""
end_keystream=[]#置换选择2后的最终子密钥列表
for key in cyc_keystream: #逐个取每轮的子密钥
temp=[]
for i in PC_2:
temp.append(key[i-1])
end_keystream.append(temp)
temp=[]
#print(len(end_keystream),len(end_keystream[0]))
for keys in end_keystream:#提取密钥为字符串
str1=''
for keystr in keys:
str1+=keystr
temp.append(str1)
#print(temp)
self.reverse2=temp
def strToBin(self,string):
"""
args{
string:二进制字符串转数值
}
"""
k=len(string)-1
values=0
for i in string:
values=int(i)*pow(2,k)+values
k=k-1
return values
def cycleLeft(self,keyList,cycleNum):
"""
args{
keyList:需要移位的密钥列表
len:移位位数
}
"""
return keyList[cycleNum:]+keyList[:cycleNum]
def get_plainGroup(self,model,plainText):
"""
获取明文并按64位进行分组
args{
plainText:明文字符串
model:模式选择,1十六进制形式,2字符串形式
}
"""
plainStr=''
self.plainText=[]
if model==1:
for c in plainText:
if c >= '0' and c <= '9':
c=ord(c)-48
plainStr+= bin(c).replace('0b','').zfill(4)#单个字符不足4位补0
elif c >='A' and c <= 'F':
c=ord(c)-55
plainStr+= bin(c).replace('0b','').zfill(4)#单个字符不足4位补0
elif model==2:
for c in plainText:
plainStr+= bin(ord(c)).replace('0b','').zfill(8)
#print('明文长度:',len(plainStr))
#不足64位用0补齐
if len(plainStr) <= 64:
for i in range(64-len(plainStr)):
plainStr+='0'
self.plainText.append(plainStr)
else: #大于64位则分组
groupNum=len(plainStr)//64 #每组64位,确定要分多少组
if len(plainStr)%64 !=0:
#print(groupNum)
groupNum+=1
for i in range(groupNum*64-len(plainStr)):
plainStr+='0'
k=0 #用于定位分组
for n in range(groupNum):
temp=''
for i in range(64):
temp+=plainStr[k+i]
k+=64
self.plainText.append(temp)
#print('明文二进制:',self.plainText)
def initReverse(self):
#明文进行初始置换
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]
self.IPreverse=[]
for i in self.plainText:
temp=''
for j in IP:
temp+=i[j-1]
self.IPreverse.append(temp)
#print('初始置换后:',self.IPreverse)
def Encrypt_Decrypt(self,model=0):
"""
轮加密
args{
model:为0时加密;为1时解密
}
"""
if model==0:
self.key=self.reverse2
elif model==1:
self.key=self.reverse2[::-1]
print('明文:',self.covertHex(self.plainText[0]))
print('初始置换后:',self.covertHex(self.IPreverse[0]))
print('轮序(i) '+'L(i) ' +'R(i) '+'key(i)')
self.cipherText=[]
for i in self.IPreverse:
R=[]
count=0 #计数当前加密轮次
left=i[:32]
right=i[32:]
R.append(right) #用于更新left
for key in self.key:
right=self.extend_reverse(right)
xor_1=xor_2=''
for j in range(0,len(right),8):#右部扩展后与密钥进行异或
x=self.strToBin(right[j:j+8])
y=self.strToBin(key[j:j+8])
z=x^y
xor_1+=bin(z).replace('0b','').zfill(8)
temp=self.instead_choose(xor_1) #s盒处理进行压缩为32位
temp=self.P_revrese(temp) #p置换
#左部与处理后的右部进行异或输出
for j in range(0,len(temp),8):
x=self.strToBin(temp[j:j+8])
y=self.strToBin(left[j:j+8])
z=x^y
xor_2+=bin(z).replace('0b','').zfill(8)
R.append(xor_2)
right=xor_2 #下一轮加密,输出作为下一轮右部输入
left=R[count]
print('轮序 '+str(count+1),self.covertHex(left),self.covertHex(right),self.covertHex(key))
count+=1 #计数当前加密轮次
print('左右交换后:',self.covertHex(right),self.covertHex(left))
print('左右部分合在一起',self.covertHex(right)+self.covertHex(left))
ciphertext=right+left #左右交换
ciphertext=self.reInitReverse(ciphertext) #逆初始置换
str1=self.covertHex(ciphertext) #将密文转为十六进制
print('逆初始置换后(密文):',str1)
self.cipherText.append(str1)
text=''
for i in range(len(self.cipherText)):
text+=self.cipherText[i]
print("密文为:",text)
def extend_reverse(self,str1):
"""
str:待扩展置换的明文字符串(右半部32位)
扩展为48位
"""
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]
temp=''
for i in E:
temp+=str1[i-1]
return temp
def instead_choose(self,strBit):
"""
代换选择,将右部和密钥异或后的值48位通过s盒压缩为32位
strBit:48位比特流
"""
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]]]
sbox_narrow=''
for i in range(0,len(strBit),6):
S_index=temp=''
temp=strBit[i:i+6]
S_index=temp[0]+temp[5]
row=self.strToBin(S_index)
column=self.strToBin(temp[1:5])
sbox_narrow+=bin(S[i//6][row][column]).replace('0b','').zfill(4)
return sbox_narrow
def P_revrese(self,str1):
"""
s盒处理后的p置换
str1:s盒处理后的32位bit
"""
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]
temp=''
for i in P:
temp+=str1[i-1]
return temp
def reInitReverse(self,str):
"""
逆初始置换IP-1
"""
IP_re= [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]
temp=''
for i in IP_re:
temp+=str[i-1]
return temp
def covertHex(self,string):
"""
string:待转换的二进制比特流
返回十六进制字符串
"""
str1=''
for c in range(0,len(string),4):
x=hex(self.strToBin(string[c:c+4]))
str1+=str(x).replace('0x','')
return str1.upper()
if __name__ == "__main__":
start=time.time()
test=DES()
test.extract_keystream(1,"AABB09182736CCDD")#获取密钥
test.reverse_function1()#密钥置换1
test.reverse_function2()#密钥置换2
print('***************加密***************')
test.get_plainGroup(1,"123456ABCD132536456789")#获取明文
test.initReverse()#初始置换
test.Encrypt_Decrypt()#加密
print('***************解密***************')
test.get_plainGroup(1,"C0B7A8D05F3A829CEDED4AC8BBEE63AF")#获取密文E157B8BD50A089F1F78EC819B8149635688D54C5FE717AD9
test.initReverse()#初始置换
test.Encrypt_Decrypt(1)#解密
#time.sleep(5)
end=time.time()
print('程序运行时间为:{}s'.format(end-start))
运行结果为:
小结:实验结果与预期一致;通过实验对DES对称密码的加密原理和流程有了清晰的了解。