抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

本文学习对称密码学的基础概念,分组密码的四种工作方式,以及详细介绍DES、AES的加密算法过程与Python实现。

概述:

对称密码

  1. 要求加密与解密使用同一个共享密钥,因此要求通信双方必须在通信前商定密钥并妥善保存。
  2. 对称密码体制从工作方式上可以分为分组加密和序列密码两大类。
  3. 优点:算法公开、计算量小、加密速度快、加密效率高
  4. 缺点:交易双方都使用同样钥匙,安全性得不到保证

序列密码(流加密):对明文的单字节进行运算,明文加密后密码保持和明文同样长度,解密是指用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。常见流密码算法有RC4、SEAL等。

分组密码(块加密):将明文信息划分成不同的块,分别对每个块进行运算。多用于网络加密。分块长度越长,密码分析越困难,越不利于操作和运算,分块长度应该权衡安全性与实用性。常见分组密码算法有DES、AES等。

DES、3DES、AES

常见对称加密算法包括DES、3DES、AES。

DES(Data Encryption Standard) 数据加密标准,速度较快,适用于加密大量数据的场合。

3DES(Triple DES) 基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高、

AES(Advanced Encryption Standard) 高级加密标准,下一代的加密算法,速度、安全级别更高,支持128,192,256,512位密钥的加密。

算法特征:

  1. 加密方与解密方使用同一个密钥。
  2. 加密解密的速度比较快,适合数据比较大时的使用。
  3. 密钥传输的过程不安全,且容易被破解,密钥管理比较麻烦。

分组加密的四种工作方式

  • ECB(Electronic Code Book) 电子编码本
  • CBC(Cipher Block Chaining) 密码分组链接
  • CFB(Cipher Feedback) 密码反馈
  • OFB(Output Feedback) 输出反馈

ECB

ECB模式是最简单的加密算法,明文消息被分成固定大小的块,并且每个块被单独加密。每个块的加密与解密都是独立的,且使用相同的方式进行加密,所以可以进行并行计算。但是这种方法存在缺点,一旦有一个块被破解,使用相同的方式可以解密其他块,导致可以解密出所有明文数据,安全性比较差。

适用场景:数据较少,加密前需要把明文数据填充到块大小的整数倍。

优点:简单、孤立,每个块单独运算。适合并行运算,传输错误一般只影响当前块。

缺点:同明文输出同密码,可能导致明文攻击。

ECB Encryption示意图

image-20200425154143112

ECB Decryption示意图

image-20200425154453598

CBC

CBC模式中每一个分组要先和前一个分组加密后的数据进行XOR异或操作,然后再进行加密。因此每个密文块依赖该块之前的所有明文块,为了保持每条消息都具有唯一性,第一个数据块进行加密之前需要用初始化向量IV进行异或操作。

适用场景:最常见的加密方式,并且与ECB一样消息块必须填充到块大小的整倍数。

优点:串行化运算,相同明文不同密文。

缺点:需要初始向量。加密是连续的,不能并行处理。

CBC Encryption示意图

image-20200425155715249

CBC Decryption示意图

image-20200425155746741

CFB

CFB模式与CBC模式比较相似,前一个分组的密文加密后和当前分组的明文XOR异或操作生成当前分组的密文。(与CBC模式仅在异或的顺序上不同)

优点:同明文不同密文,分组密钥转换为流密码

缺点:串行运算不利并行,传输错误可能导致后续传输块的错误

CFB Encryption示意图

image-20200425160439178

CFB Decryption示意图

image-20200425161118871

OFB

OFB模式将分组密码转换为同步流密码(根据明文长度先独立生成相应长度的流密码),与CFB模式非常相似,CFB是前一个分组的密文加密后XOR当前分组明文。OFB是初始化向量IV不断加密后XOR前一组的明文。由于异或操作的对称性,OFB模式的解密与加密的流程一样。

优点:同明文不同密文,分组密钥转换为流密码

缺点:串行运算不利并行,传输错误可能导致后续传输块的错误。

OFB Encryption示意图

image-20200425162017815

OFB Decryption示意图

image-20200425162037210

DES加密过程

概述

采用56位密钥加密64位的明文数据

image-20200425222311122

当n个64位明文数据块都经过DES加密处理(这里采用上述四种工作方式中的ECB模式)后,所得到的n个64位密文数据块串在一起就是密文输出。

image-20200425222419777

加密流程图

Round:每一轮都进行一次扩展置换、S盒压缩和P盒置换。

image-20200425222807887

三个知识难点

  1. 初始(终止)置换
  2. 扩展置换
  3. S盒压缩

初始(终止)置换

初始和终止置换都是按照一定的规则,将原来的64位二进制重新排序。

初始置换与终止置换表

image-20200425225113206

从初始置换表中可以看出,将输入的58位换到第1位,第50位换到第2位,以此类推

从终止置换表中可以看出,将输入的40位换到第1位,第08位换到第2位,以此类推

举例例子来说明

根据上述初始置换与终止置换表,给定原始数据(一般是16位十六进制数,下图已将其转化为64位二进制数)

image-20200425225903387

计算置换后的数据

image-20200425231009622

终止置换的原理和初始置换是一样的,他们的排列是互逆的,即经过一次初始置换和终止置换,64位二进制数就还原了。

扩展置换

经过初始置换的64位数据被分成左右32位数据,其中一边的32位进行扩展置换得到48位的数据。

image-20200426000243820

具体的扩展过程就是在某些位置进行添位操作。

image-20200426000416940

我们仍可以参照初始数据置换表类似的扩展置换表。

扩展置换表

image-20200426000629416

根据这32位二进制数据来展示扩展置换的过程.

将原始数据分成8组,接着将每个组从4位扩展成6位的组。

1
2
1101 0001 0011 0100
0010 0011 0011 1011

根据扩展字段表

第一组的头添位是原始数据的第32位1,第一组的尾添位是原始数据的第05位0

第二组的头添位是原始数据的第04位1,第一组的尾添位是原始数据的第09位0

…依次类推

这样就完成从32位扩展到48位的扩展置换过程。

1
2
111010 100010 100110 101000
000100 000110 100111 110111

S盒压缩处理

经过扩展的48位明文和48位密钥进行异或运算后,再使用8个S盒压缩处理得到32位数据。

image-20200426113630923

实际上就是将48位输入等分成8块,每块6位输入压缩为4位输出。与前面置换表类似的,具体处理时是分别采用8张4行16列的表来进行替换从而达到压缩的目的。

image-20200426113658219

举例例子来说明

输入原始数据,并将其分成8组,每组6位。

1
2
3
4
111111 100101
110011 001100
011001 010111
111011 011000
第一组的压缩表

image-20200426120313750

我们以第一组为例来演示S盒压缩处理

1
2
3
4
5
原始数据:111111
头尾数据(第0位和最后一位):11 -> 3 行数
中间四位:1111 -> 15 列数
根据行数和列数即可在压缩表中找到对应的压缩数13
再转化成二进制1101。

在全部组完成S盒压缩,则是从48位转换到32为的S盒压缩处理过程。

P盒置换

经过S盒压缩处理后,再经过P盒置换,就得到一轮运算中的最后输出了。

image-20200426120620592

DES Python实现

若需要在Python中使用des加密,可以直接使用pyDes库加密,该库提供了CBC和ECB两种加密方式。(注:安装pyDes库 pip install pyDes

pyDes.des(key, [mode], [IV], [pad], [padmode])

  • Key 输入密钥
  • mode ECB或者CBC
  • IV 初始密钥8位(在使用CBC模式时)
  • pad/padmode 默认None/PAD_NORMAL

需要注意的是在说明文档里写着Python3使用字节而不是字符串。

image-20200426123246292

以下为本人使用的例子,采用CBC加密方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
import pyDes

Des_key = b"ABCDEFGH" # 密钥
Des_IV = b"\x33\x22\x81\x35\x38\xBC\xE7\x5A" #自动IV向量

def DesEncrypt(str):
k = pyDes.des(Des_key, pyDes.CBC, Des_IV, pad=None, padmode=PAD_PKCS5)
EncryptStr = k.encrypt(str)

return base64.b64encode(EncryptStr)

DesEncrypt(b"let do it")
1
Output: b'Ub48xNwxZ/B8ZpEvpO+Mwg=='

AES加密过程

image-20200426134519614

AES加密方式有五种:ECB, CBC, CTR, CFB, OFB

从安全性角度推荐CBC加密方法,本文介绍了CBC,ECB两种加密方法的python实现

python 在 Windows下使用AES时要安装的是pycryptodome 模块 pip install pycryptodome

python 在 Linux下使用AES时要安装的是pycrypto模块 pip install pycrypto

CBC加密需要一个十六位的key(密钥)和一个十六位iv(偏移量)

ECB加密不需要iv

AES-CBC Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex


# 如果text不足16位的倍数就用空格补足为16位
def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')


# 加密函数
def encrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_CBC
iv = b'qqqqqqqqqqqqqqqq'
text = add_to_16(text)
cryptos = AES.new(key, mode, iv)
cipher_text = cryptos.encrypt(text)
# 因为AES加密后的字符串不一定是ascii字符集的,输出保存可能存在问题,所以这里转为16进制字符串
return b2a_hex(cipher_text)


# 解密后,去掉补足的空格用strip() 去掉
def decrypt(text):
key = '9999999999999999'.encode('utf-8')
iv = b'qqqqqqqqqqqqqqqq'
mode = AES.MODE_CBC
cryptos = AES.new(key, mode, iv)
plain_text = cryptos.decrypt(a2b_hex(text))
return bytes.decode(plain_text).rstrip('\0')


if __name__ == '__main__':
e = encrypt("hello world") # 加密
d = decrypt(e) # 解密
print("加密:", e)
print("解密:", d)

AES-ECB Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
"""
ECB没有偏移量
"""
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex


def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')


# 加密函数
def encrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_ECB
text = add_to_16(text)
cryptos = AES.new(key, mode)

cipher_text = cryptos.encrypt(text)
return b2a_hex(cipher_text)


# 解密后,去掉补足的空格用strip() 去掉
def decrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_ECB
cryptor = AES.new(key, mode)
plain_text = cryptor.decrypt(a2b_hex(text))
return bytes.decode(plain_text).rstrip('\0')


if __name__ == '__main__':
e = encrypt("hello world") # 加密
d = decrypt(e) # 解密
print("加密:", e)
print("解密:", d)

评论