DES加密算法原理及Python代码实现
写在前面: 1、本文中DES加解密基本流程及S盒等参数参照自杨波《现代密码学(第四版)》,实现过程均为自编函数。 2、为了说明64bit密钥中,只有56bit真正参与加解密过程,对网上代码中的密钥生成过程做出了修改,详见正文。 3、本文借鉴了网上部分代码,具体见参考文献,并对部分地方按题主想法进行了优化修改。1. DES算法理论介绍 具体可参见杨波《现代密码学(第四版)》。本文只做简要介绍
写在前面:
1、本文中DES加解密基本流程及S盒等参数参照自杨波《现代密码学(第四版)》,实现过程均为自编函数。
2、为了说明64bit密钥中,只有56bit真正参与加解密过程,对网上代码中的密钥生成过程做出了修改,详见正文。
3、本文借鉴了网上部分代码,具体见参考文献,并对部分地方按题主想法进行了优化修改。
1. DES算法理论介绍
具体可参见杨波《现代密码学(第四版)》。本文只做简要介绍。
1.1 DES介绍
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
1.2 DES加解密算法描述
图1.2是DES加密变换框图,其中明文一组为64bit,密钥K长度56bit。加密过程有3个阶段:
1、初始置换IP,用于重排明文分组的64比特数据。
2、经过具有相同功能的16轮Feistel变换,每轮中F函数中都有置换和代换运算,第16轮变换的输出分为左右两半,并被交换次序。
3、经过一个逆初始置换IP-1(为IP的逆)从而产生64比特的密文。
1.2.1 轮结构
采用Feistel相同的轮结构,将64bit的轮输入分为32bit的左、右两半,分别记为L和R:
L
i
=
R
i
−
1
L_i=R_{i-1}
Li=Ri−1
R
i
=
L
i
−
1
⊕
F
(
R
i
−
1
,
K
i
)
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ R_i=L_{i-1}\oplus F\left( R_{i-1},K_i \right)
Ri=Li−1⊕F(Ri−1,Ki)
其中,F函数示意图见图1.2.1。
1.2.2 密钥的生成
图1.2.2是使用56比特密钥的方法。密钥首先通过一个置换函数PC_1,然后,对加密过程的每一轮,通过一个左循环移位和一个置换PC_2产生一个子密钥。其中每轮的置换都相同,但由于密钥被重复迭代,所以产生的每轮子密钥不相同。
1.2.3 DES解密
和Feistel密码一样,DES的解密和加密使用同一种算法,但子密钥使用的顺序相反。
2. Python代码实现
2.1 实现思路
由于加密与解密算法类似,此处给出加密算法实现思路:
Step1:从文件中读取明文;
Step2:将明文利用ASCII换成01比特流;
Step3:将明文比特流每64位分为一组,最后不足的用0补齐;
Step4:对64位比特加密操作(进行IP置换,16轮的Feistel变换、交换L、R、IP逆置换,将L、R合并为密文比特流);
Step5:将每一组密文比特流合并转换成密文字符保存至文件。
2.2 模块化程序设计
2.2.1 各个模块调用关系及实现功能设计
根据2.1中的实现思路,DES加解密算法各个模块调用关系及实现功能设计如图2.2.1所示,2.2.2中将详细介绍各个功能的代码实现。
2.2.2 模块功能实现
针对2.2.1的实现思路,分析DES加解密算法流程,可将其具体分成不同的模块分别设计:
1.)文件读入模块
def read_file(filename):
'''
filename : 打开文件名
return : 读取文件中字符串
'''
try:
fp = open(filename,"r",encoding='utf-8')
message = fp.read()
fp.close()
return message
except:
print("Open file error!")
2.)文件写入模块
将字符串写入文件text.txt与文件读入代码类似,只做如下修改:
fp = open('text.txt','w',encoding='utf-8')
fp.write(message)
def write_file(message): 输入需要写入字符串,即可生成含有该字符串的text.txt文件。
3.)字符串转01比特流
def str_bit( message ):
'''
message :字符串
return :将读入的字符串序列转化成01比特流序列
'''
bits = ""
for i in message:
asc2i = bin(ord(i))[2:] #bin将十进制数转二进制返回带有0b的01字符串
'''为了统一每一个字符的01bit串位数相同,将每一个均补齐8位'''
for j in range(8-len(asc2i)):
asc2i = '0' + asc2i
bits += asc2i
return bits
每一个字符利用ord( )函数转化成对应ASCII值,利用bin( )将其转换成二进制字符串。
4.)01比特流转字符
def bit_str(bits): 输入01比特串(长度要是8的倍数),返回对应的字符,核心代码如下,主要利用int( )和chr( )函数。
for i in range(len(bits)//8):
temp += chr(int(bits[i*8:(i+1)*8],2))
5.)密钥字符串转比特流
本文假定密钥比特流的8、16、24、32、40、48、56、64位采用偶校验方式,分别校验其前面的7位01串。密钥字符串依然采用ASCII编码方式,一个字符占7位,第8位采用偶校验方式,核心代码如下:
def process_key(key):
'''
key : 输入的密钥字符串
return : 64bit 01序列密钥(采用偶校验的方法)
'''
key_bits = ""
for i in key:
count = 0
asc2i = bin(ord(i))[2:]
'''将每一个ascii均补齐7位,第8位作为奇偶效验位'''
for j in asc2i:
count += int(j)
if count % 2 == 0:
asc2i += '0'
else:
asc2i += '1'
for j in range(7-len(asc2i)):
asc2i = '0' + asc2i
key_bits += asc2i
if len(key_bits) > 64:
return key_bits[0:64]
else:
for i in range(64-len(key_bits)):
key_bits += '0'
return key_bits
6.)对比特流分组
函数定义如下,最后一组位数不足即补0。实现简单,此处不在赘述。
def divide(bits,bit):
'''
bits : 将01bit按bit一组进行分组
return : 按bit位分组后得到的列表
'''
7.)IP置换
为了实现简单,提前将IP、IP_RE、PC_1、PC_2、E、P、S等盒值写入文件DES_BOX.py,主函数即可直接调用。IP置换实现如下:
def IP_change(bits):
'''
bits:一组64位的01比特字符串
return:初始置换IP后64bit01序列
'''
ip_str = ""
for i in IP:
ip_str = ip_str + bits[i-1]
return ip_str
8.)PC_1置换
实现代码同IP置换,此处不在赘述。
9.)比特串左移
def key_leftshift(key_str,num): 将输入的01比特流key_str循环左移num位返回,实现过于简单,此处不在赘述。
10.)PC_2置换
实现代码同IP置换,此处不在赘述。
11.)16轮密钥生成
def generate_key(key):
'''
key : 64bit01密钥序列
return : 16轮的16个48bit01密钥列表按1-16顺序
'''
key_list = ["" for i in range(16)]
key = PC_1_change(key) #1、调用置换PC_1
key_left = key[0:28] #2、左右28位分开
key_right = key[28:]
for i in range(len(SHIFT)): #共16轮即16次左循环移位
key_left = key_leftshift(key_left, SHIFT[i]) #3、调用比特串左移函数
key_right = key_leftshift(key_right, SHIFT[i])
key_i = PC_2_change(key_left + key_right) #4、左右合并调用置换PC_2
key_list[i] = key_i #5、将每一轮的56bit密钥存入列表key_list
return key_list
12.)E置换
实现代码同IP置换,此处不在赘述。
13.)异或运算
函数实现较为简单,将输入的两个字符串逐位运算即可,仅给出定义如下。
def xor(bits,ki):
'''
bits : 48bit01字符串 / 32bit01 F函数输出
ki : 48bit01密钥序列 / 32bit01 Li
return :bits与ki异或运算得到的48bit01 / 32bit01
'''
14.)单次S盒查找
def s(bits,i):
'''
bits : 6 bit01字符串
i : 使用第i个s盒
return : 4 bit01字符串
'''
row = int(bits[0]+bits[5],2)
col = int(bits[1:5],2)
num = bin(S[i-1][row*16+col])[2:] #i-1号S盒的row*16+col号数
for i in range(4-len(num)): #补齐4位后输出
num = '0'+num
return num
15.)S盒变换
def S_change(bits): 输入48bit字符串,输出经过S盒之后的32bit字符串。核心代码如下,调用8次单次S盒查找函数:
for i in range(8):
temp = bits[i*6:(i+1)*6]
temp = s(temp,i+1)
s_change += temp
16.)P置换
实现代码同IP置换,此处不在赘述。
17.)F函数
通过调用12-16模块即可实现第
i
i
i轮F函数运算:
def F(bits,ki):
'''
bits : 32bit 01 Ri输入
ki : 48bit 第i轮密钥
return : F函数输出32bit 01序列串
'''
bits = xor(E_change(bits),ki)
bits = P_change(S_change(bits))
return bits
18.)IP逆置换
实现代码同IP置换,此处不在赘述。
19.)64bit加密
调用IP置换、16轮密钥生成、F函数、异或运算、IP逆置换等模块可以实现64bit一组明文的加密:
def des_encrypt(bits,key):
'''
bits : 分组64bit 01明文字符串
key : 64bit01密钥
return : 加密得到64bit 01密文序列
'''
bits = IP_change(bits) # IP置换
L = bits[0:32] # 切片分成两个32bit
R = bits[32:]
key_list = generate_key(key) # 生成16个密钥
for i in range(16): # 16轮迭代变换
L_next = R
R = xor(L,F(R,key_list[i]))
L = L_next
result = IP_RE_change( R + L) # IP逆置换
return result
20.)64bit解密
def des_decrypt(bits,key):该模块与64bit加密模块流程相同,不同在于16个密钥使用顺序相反,16轮代换代码如下,其余代码同加密。
for i in range(16):
L_next = R
R = xor(L,F(R,key_list[15-i]))
L = L_next
21.)整体加密模块
def all_des_encrypt(message,key): 读入明文字符串message,以及密钥字符串key,返回加密后01比特流。通过调用字符串转01比特流、密钥字符串转比特流、对比特流分组、64bit加密等模块即可实现:
def all_des_encrypt(message,key):
'''
message : 读入明文字符串
key : 读入密钥串
returns : 密文01序列
'''
message = str_bit(message) # 明文转01比特流
key = process_key(key) # 64bit初始密钥生成
mess_div = divide(message, 64) # 明文按64bit一组进行分组
result =""
for i in mess_div:
result += des_encrypt(i, key) #对每一组进行加密运算
return result
22.)整体解密模块
def all_des_decrypt(message,key): 读入明文字符串message,以及密钥字符串key,返回解密后01比特流。与加密类似,此处不在赘述。
2.2.3 主模块
输出提示语,并与用户交互。通过调用文件读入或写入、字符串转01比特流、比特流转01字符串、加解密等模块实现,伪代码如下:
2.3 Python源代码
全部代码及相关注释点这里github。
2.4 关于密钥的说明
起初按如下方式生成64bit密钥,即将密钥中每一个字符转成8bitASCII码,8个字符共构成64bit密钥。
def process_key(key):
bin_key = str_bit(key) #调用字符串转01比特流模块
return bin_key
结果发现“wuzhenll”和“vt{idomm”这两个密钥加密后的密文相同。
这是因为表面上这两个密码迥然不同,但是由于它们仅在奇偶校验位上有区别。例如,w的ASCII为01110111,v的ASCII为01110110,仅仅只在最后一位有区别。
由于64位密钥中的第8位、第16位、第24位、第32位、第40位、第48位、第56位、第64位作为奇偶校验位,在PC_1置换时去掉了这8位。如果输入的密码只是在这8位上有区别的话,那么操作后的结果也将是一样的。所以用这两个密码进行加密解密操作得到的结果是一样的。
2.5 使用说明
运行环境: Python 3.7
使用方法: 将DES_BOX.py、DES.py、以及需要含有明密文的文本文件放置于同一目录下,运行DES.py程序,根据相应提示语即可完成操作。明文加密后乱码会自动保存到text.txt文件中。(注:输入的密钥将转化成对应的ASCII按照偶校验的方式构成64bit初始密钥)
2.6 结果测试
测试一:明文:keep early hours! 密钥:password
测试二: 明文:Have a good day! 密钥:messagee
参考文献
更多推荐
所有评论(0)