1. 前言
DES 算法是一种常见的分组加密算法,由IBM公司在1971年提出。DES 算法是分组加密算法的典型代表。其加密运算、解密运算使用相同的秘钥。
DES 算法利用56+8奇偶校验位(第8,16,24,32,40,48,56,64)=64位的密钥对以64位为单位的块数据进行加解密。
DES算法是以64位为一组分组加密的,所以当输入的明文长度,不满足64的整数倍时,需要涉及到数据的填充。
DES算法将输入明文分为N个64位大小的分组,然后依次根据加密运算,生成N组加密后的密文 ,将生成的N组密文,就是我们要最终的成果——DES密文输出。
包含DES算法在内的现代密码学知识可以查看以下博客
CISSP考试要求里的“应用密码学”内容辅助记忆趣味串讲_晓翔仔的博客-CSDN博客
2. DES加密原理
2.1 初始置换
初始置换就是将明文M打乱重排序。并生成L0 , R0
2.2 生成子秘钥
DES加密的过程,明文M共经历了16次运算迭代,每次迭代的数据长度是48 ,因此我们需要16组48位的加密秘钥来进行加密。
PC-1置换
PC-2置换
2.3 迭代的过程
f函数
f函数就是不断计算Li,Ri的过程,此过程包括四个部分:E盒置换、S盒置换、P盒置换、左右校验
2.4 逆置换
2.5 解密
解密过程同加密过程,只不过是按照反向的次序。
2.6 数据填充
当明文长度不为分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组长度。
NoPadding
API或算法本身不对数据进行处理,加密数据由加密双方约定填补算法。例如若对字符串数据进行加解密,可以补充\0或者空格,然后trim
PKCS5Padding
加密前:数据字节长度对8取余,余数为m,若m>0,则补足8-m个字节,字节数值为8-m,即差几个字节就补几个字节,字节数值即为补充的字节数,若为0则补充8个字节的8
解密后:取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文。
加密字符串为为AAA,则补位为AAA55555;加密字符串为BBBBBB,则补位为BBBBBB22;加密字符串为CCCCCCCC,则补位为CCCCCCCC88888888。
PKCS7Padding
PKCS7Padding 的填充方式和PKCS5Padding 填充方式一样。只是加密块的字节数不同。PKCS5Padding明确定义了加密块是8字节,PKCS7Padding加密快可以是1-255之间。
2.7 加密模式
电子代码本(Electronic CodeBook, ECB)模式
一般用途:加密少量数据,如秘钥和PIN值
密码分组链接(Cipher Block Chaini吨, CBC)模式
一般用途:加密大型数据
密码回馈(Cipher FeedBack, CFB)模式
一般用途:加密较小数据,单独加密每个位
输出回馈(Output FeedBack, OFB)模式
特点:比OFB发生扩展错误的可能性小
计数(CounTeR, CTR)模式。
特点:IV计数器,并行
参考
这篇博客将DES加密原理讲的通俗易懂,值得阅读。
DES加密算法原理及代码实现_des加密算法代码_better_hui的博客-CSDN博客
3. 代码实现
3.1 ECB和CBC模式的加解密代码
from pyDes import des, CBC, PAD_PKCS5
import binascii
def bytesToHexString(data):
temp = []
for i in data:
temp.append('0x%02X' % i)
return temp
"""
DES-ECB 加密
:param s: 原始字符串,密钥
:return: 加密后字符串,16进制
"""
def des_encrypt(s, key):
secret_key = key
iv = secret_key
k = des(secret_key)
en = k.encrypt(s)
return binascii.b2a_hex(en)
"""
DES-ECB 解密
:param s: 原始字符串,密钥
:return: 解密后字符串,16进制
"""
def des_decrypt(s, key):
secret_key = key
iv = secret_key
k = des(secret_key)
en = k.decrypt(s)
return binascii.b2a_hex(en)
"""
DES-CBC 加密
:param s: 原始字符串,密钥
:return: 加密后字符串,16进制
"""
def des_cbc__encrypt(s, key, iv):
secret_key = key
k = des(secret_key, mode=CBC, IV=iv)
en = k.encrypt(s)
return binascii.b2a_hex(en)
"""
DES-CBC 加密
:param s: 原始字符串,密钥
:return: 加密后字符串,16进制
"""
def des_cbc__decrypt(s, key, iv):
secret_key = key
k = des(secret_key, mode=CBC, IV=iv)
en = k.decrypt(s)
return binascii.b2a_hex(en)
if __name__ == '__main__':
data = '1111111111111111'
key = '1111111111111111'
iv = '1111111111111111' # iv 仅仅用于cbc模式,若iv设置为全0,则CBC模式的计算结果和ECB模式是一样的
print("data:", data, "key:", key, "iv:", iv)
print("des ecb encrypt result:", des_encrypt(bytes.fromhex(data), bytes.fromhex(key)))
print("des ecb decrypt result:", des_decrypt(bytes.fromhex(data), bytes.fromhex(key)))
print("des cbc encrypt result:",
des_cbc__encrypt(bytes.fromhex(data), bytes.fromhex(key), bytes.fromhex(iv)))
print("des cbc decrypt result:",
des_cbc__decrypt(bytes.fromhex(data), bytes.fromhex(key), bytes.fromhex(iv)))
自测结果
data: 1111111111111111 key: 1111111111111111 iv: 1111111111111111
des ecb encrypt result: b'f40379ab9e0ec533'
des ecb decrypt result: b'237b2304c393d3ac'
des cbc encrypt result: b'82e13665b4624df5'
des cbc decrypt result: b'326a3215d282c2bd'
Process finished with exit code 0
3.2 一种应用场景下的DES解码编码python代码
已知:
1.使用DES ECB密码算法,秘钥已经获取到。
2. 获取到"明文前缀“+密文",都是十六进制表示,密文由"固定长度的固定位 + 时间戳+ crc"组成。crc 使用ccitt_false模式。
需要:
明文前缀不变,密文DES解码后,更新时间戳为最新,并重新计算crc,再加密生成转发数据。
python程序:
import pyDes
from pyDes import des, CBC, PAD_PKCS5, ECB
import binascii
import datetime
from time import time
from binascii import unhexlify
from crcmod import mkCrcFun
# CRC16/CCITT
def crc16_ccitt(s):
crc16 = mkCrcFun(0x11021, rev=True, initCrc=0x0000, xorOut=0x0000)
return get_crc_value(s, crc16)
# CRC16/CCITT-FALSE
def crc16_ccitt_false(s):
crc16 = mkCrcFun(0x11021, rev=False, initCrc=0xFFFF, xorOut=0x0000)
return get_crc_value(s, crc16)
# CRC16/MODBUS
def crc16_modbus(s):
crc16 = mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000)
return get_crc_value(s, crc16)
# CRC16/XMODEM
def crc16_xmodem(s):
crc16 = mkCrcFun(0x11021, rev=False, initCrc=0x0000, xorOut=0x0000)
return get_crc_value(s, crc16)
# common func
def get_crc_value(s, crc16):
data = s.replace(' ', '')
crc_out = hex(crc16(unhexlify(data))).upper()
str_list = list(crc_out)
if len(str_list) == 5:
str_list.insert(2, '0') # 位数不足补0
crc_data = ''.join(str_list[2:])
# return crc_data[:2] + ' ' + crc_data[2:]
return crc_data[:2] + crc_data[2:]
def bytesToHexString(data):
temp = []
for i in data:
temp.append('0x%02X' % i)
return temp
"""
DES-ECB 加密
:param s: 原始字符串,密钥
:return: 加密后字符串,16进制
"""
def des_encrypt(s, key):
secret_key = key
iv = secret_key
k = des(secret_key, pyDes.ECB, IV=iv, padmode=pyDes.PAD_PKCS5)
en = k.encrypt(s)
return binascii.b2a_hex(en)
"""
DES-ECB 解密
:param s: 原始字符串,密钥
:return: 解密后字符串,16进制
"""
def des_decrypt(s, key):
secret_key = key
iv = secret_key
k = des(secret_key, pyDes.ECB, IV=iv, padmode=pyDes.PAD_PKCS5)
en = k.decrypt(s)
return binascii.b2a_hex(en)
if __name__ == '__main__':
key = '1111111111111111'
input_data = '0101e1618051991f4520329c8dec711dd986f7ebf5dcdf7aeec6'
print("输入报文:", input_data)
fix_mingwen = input_data[:4]
print("输入数据的明文部分是", fix_mingwen)
input_data_miwen = input_data[4:]
print("输入数据的密文部分是:", input_data_miwen)
data_body = repr(des_decrypt(bytes.fromhex(input_data_miwen), bytes.fromhex(key)))[2:-1]
print("解密后的明文:", data_body)
# 计算此刻的时间戳
timestamp = hex(int(time())).strip("0x")
print("此时的时间戳:", timestamp)
# 计算明文
data_body = repr(data_body)[1:29] + timestamp
#crc16 计算有多种模式,这里用crc16_ccitt_false
crc16_result = crc16_ccitt_false(data_body)
data_body = data_body + crc16_result
print("更新的时间戳的明文:", data_body)
data_body_secrect = des_encrypt(bytes.fromhex(data_body), bytes.fromhex(key))
data_body_secrect = repr(data_body_secrect)[2:-1]
print("加密后的密文: ", data_body_secrect)
data_body_secrect_all = fix_mingwen + data_body_secrect
print("输出加密后的完整报文:", data_body_secrect_all)
data_body_length = len(data_body_secrect_all) // 2
dataList = []
for i in range(0, data_body_length):
dataList.append(data_body_secrect_all[2 * i] + data_body_secrect_all[2 * i + 1]) # 每个字节由相邻两个16机制字符组成
data_body_kongge = " ".join(dataList)
print("含有空格的报文表示:", data_body_kongge)
自测结果:
输入报文: 0101e1618051991f4520329c8dec711dd986f7ebf5dcdf7aeec6
输入数据的明文部分是 0101
输入数据的密文部分是: e1618051991f4520329c8dec711dd986f7ebf5dcdf7aeec6
解密后的明文: 7072656668313233343536373839644335d2ff0b
此时的时间戳: 64433e0f
更新的时间戳的明文: 707265666831323334353637383964433e0f3921
加密后的密文: e1618051991f4520329c8dec711dd986d35c06166403b220
输出加密后的完整报文: 0101e1618051991f4520329c8dec711dd986d35c06166403b220
含有空格的报文表示: 01 01 e1 61 80 51 99 1f 45 20 32 9c 8d ec 71 1d d9 86 d3 5c 06 16 64 03 b2 20
Process finished with exit code 0
4. 最后
DES由于密钥过短,并不认为是安全的密码学算法。一般推荐更新为3DES算法。