一、前言
AES,高级加密标准(Advanced Encryption Standard)。是用来替代 DES,目前比较流行的对称加密算法。与上一篇博文提到过的 RSA 非对称算法不同,对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
明文 P:
等待加密的数据。
密钥 K:
用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。
AES 加密函数:
设 AES 加密函数为 E,则 C = E(K, P),其中 P 为明文,K 为密钥,C 为密文。也就是说,把明文 P 和密钥 K 作为加密函数的参数输入,则加密函数 E 会输出密文 C。
密文 C:
经过 AES 加密后的数据。
AES 解密函数:
设 AES 解密函数为 D,则 P = D(K, C),其中 C 为密文,K 为密钥,P 为明文。也就是说,把密文 C 和密钥 K 作为解密函数的参数输入,则解密函数会输出明文 P。
AES 只是个基本算法,实现 AES 有几种模式,主要有 ECB、CBC、CFB 和 OFB 这几种(其实还有个 CTR)。本章主要介绍最常用的 ECB 和 CBC 模式。
二、代码实现与解析
1.ECB 模式
from Crypto.Cipher import AES
import base64
BLOCK_SIZE = 16 # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
def aesEncrypt(key, data):
'''
AES的ECB模式加密方法
:param key: 密钥
:param data:被加密字符串(明文)
:return:密文
'''
key = key.encode('utf8')
# 字符串补位
data = pad(data)
cipher = AES.new(key, AES.MODE_ECB)
# 加密后得到的是bytes类型的数据,使用Base64进行编码,返回byte字符串
result = cipher.encrypt(data.encode())
encodestrs = base64.b64encode(result)
enctext = encodestrs.decode('utf8')
print(enctext)
return enctext
def aesDecrypt(key, data):
'''
:param key: 密钥
:param data: 加密后的数据(密文)
:return:明文
'''
key = key.encode('utf8')
data = base64.b64decode(data)
cipher = AES.new(key, AES.MODE_ECB)
# 去补位
text_decrypted = unpad(cipher.decrypt(data))
text_decrypted = text_decrypted.decode('utf8')
print(text_decrypted)
return text_decrypted
if __name__ == '__main__':
key = '5c44c819appsapi0'
data = 'herish acorn'
ecdata = aesEncrypt(key, data)
aesDecrypt(key, ecdata)
结果如下:
0FyQSXu3Q9Q13JGf4F74jA==
herish acorn
检测结果可以前往在线 AES 加密解密进行验证。
ECB 是最简单的块密码加密模式,加密前根据加密块大小(如 AES 为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。相对其他模式没有偏移量的设置,简单点,安全性差点。
AES 加密有 AES-128、AES-192、AES-256 三种,分别对应三种密钥长度 128bits(16字节)、192bits(24字节)、256bits(32字节)。当然,密钥越长,安全性越高,加解密花费时间也越长。默认的是AES-128,其安全性完全够用。
pad 和 unpad 分别是填充函数和逆填充函数。因为 AES 加密对加密文本有长度要求,必须是密钥字节数的倍数。这里的 key 的长度是 16 个字节。
关于填充算法,简单解释就是缺几位就补几:填充字符串由一个字节序列组成,每个字节填充该填充字节序列的长度。Python 代码中 ECB 模式进行 AES 加密默认就是 pkcs7padding 填充。
2.CBC 模式
CBC 模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。
from Crypto.Cipher import AES
import base64
# 密钥(key), 密斯偏移量(iv) CBC模式加密
BLOCK_SIZE = 16 # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
vi = '0102030405060708'
def AES_Encrypt(key, data):
data = pad(data)
# 字符串补位
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
encryptedbytes = cipher.encrypt(data.encode('utf8'))
# 加密后得到的是bytes类型的数据,使用Base64进行编码,返回byte字符串
encodestrs = base64.b64encode(encryptedbytes)
# 对byte字符串按utf-8进行解码
enctext = encodestrs.decode('utf8')
return enctext
def AES_Decrypt(key, data):
data = data.encode('utf8')
encodebytes = base64.decodebytes(data)
# 将加密数据转换位bytes类型数据
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
text_decrypted = cipher.decrypt(encodebytes)
# 去补位
text_decrypted = unpad(text_decrypted)
text_decrypted = text_decrypted.decode('utf8')
print(text_decrypted)
return text_decrypted
if __name__ == '__main__':
key = '5c44c819appsapi0'
data = 'herish acorn'
enctext = AES_Encrypt(key, data)
print(enctext)
AES_Decrypt(key, enctext)
结果如下:
svAg4qrFNphvwS47DLSb2A==
herish acorn
相比 ECB 模式,CBC 模式主要是多了偏移量这一要素,偏移量的取值,至少 16 位,或者为 16 的倍数。