写在前面:
  1、本文中DES加解密基本流程及S盒等参数参照自杨波《现代密码学(第四版)》,实现过程均为自编函数。
  2、为了说明64bit密钥中,只有56bit真正参与加解密过程,对网上代码中的密钥生成过程做出了修改,详见正文。
  3、本文借鉴了网上部分代码,具体见参考文献,并对部分地方按题主想法进行了优化修改。

1. DES算法理论介绍

  具体可参见杨波《现代密码学(第四版)》。本文只做简要介绍。

1.1 DES介绍

  DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

1.2 DES加解密算法描述

图 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=Ri1
                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=Li1F(Ri1,Ki)
  其中,F函数示意图见图1.2.1。

图1.2.1 F函数示意图
1.2.2 密钥的生成
图 1.2.2 16轮密钥生成图

  图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.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

参考文献

DES算法原理完整版

DES加解密python实现

DES算法中密钥的校验位

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐