utils/tools/aes_encrypt_decrypt.py

311 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
@FileNameaes_encrypt_decrypt.py
@Description
@AuthorFloraachy
@Time2024/11/22 14:48
"""
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from base64 import b64encode
from Crypto.Random import get_random_bytes
"""
参考https://blog.csdn.net/chouzhou9701/article/details/122019967
AES 加密最常用的模式就是 ECB模式 和 CBC 模式当然还有很多其它模式他们都属于AES加密。ECB模式和CBC 模式俩者区别就是 ECB 不需要 iv偏移量而CBC需要。
AES加密使用参数:
1) 秘钥: 加密的时候用秘钥,解密的时候需要同样的秘钥才能解出来; 数据类型为bytes
2) 明文: 需要加密的参数; 数据类型为bytes
3) 模式: aes 加密常用的有 ECB 和 CBC 模式(我只用了这两个模式,还有其他模式);数据类型为aes类内部的枚举量
4) iv 偏移量: 这个参数在 ECB 模式下不需要,在 CBC 模式下需要数据类型为bytes
1. 在Python中进行AES加密解密时所传入的密文、明文、秘钥、iv偏移量、都需要是bytes字节型数据。python 在构建aes对象时也只能接受bytes类型数据。
2.当秘钥iv偏移量待加密的明文字节长度不够16字节或者16字节倍数的时候需要进行补全。
3. CBC模式需要重新生成AES对象为了防止这类错误我写代码无论是什么模式都重新生成AES对象。
【编码模式】
由于python中的 AES 加密解密,只能接受字节型(bytes)数据。而我们常见的 待加密的明文可能是中文或者待解密的密文经过base64编码的这种都需要先进行编码或者解码然后才能用AES进行加密或解密。
因此在python使用AES进行加密或者解密时都需要先转换成bytes型数据。
对于中文明文我们可以使用encode()函数进行编码将字符串转换成bytes类型数据。解密后同样是需要decode()函数进行解码的,将字节型数据转换回中文字符(字符串类型)。
【填充模式】
前面我使用秘钥还有明文包括IV向量都是固定16字节也就是数据块对齐了。而填充模式就是为了解决数据块不对齐的问题使用什么字符进行填充就对应着不同的填充模式
AES补全模式常见有以下几种
1ZeroPadding 用b\x00进行填充这里的0可不是字符串0而是字节型数据的b\x00
2PKCS7Padding 当需要N个数据才能对齐时填充字节型数据为N、并且填充N个
3PKCS5Padding与PKCS7Padding相同在AES加密解密填充方面我没感到什么区别
4no padding当为16字节数据时候可以不进行填充而不够16字节数据时同ZeroPadding一样
"""
from Crypto.Cipher import AES
import base64
import binascii
# 数据类
class MData():
"""
MData类用于处理和转换数据。
它可以将数据保存到文件从字符串或Base64编码的数据中加载数据
并将数据转换为多种格式如字符串、Base64编码的字符串或十六进制字符串。
"""
def __init__(self, data=b"", characterSet='utf-8'):
"""
初始化MData对象。
参数:
data (bytes): 初始数据,默认为空字节串。
characterSet (str): 字符编码,默认为'utf-8'
"""
# data肯定为bytes
self.data = data
self.characterSet = characterSet
def saveData(self, FileName):
"""
将数据保存到指定的文件中。
参数:
FileName (str): 要保存数据的文件名。
"""
with open(FileName, 'wb') as f:
f.write(self.data)
def fromString(self, data):
"""
从字符串加载数据,并将其编码为字节串。
参数:
data (str): 要加载的字符串数据。
返回:
bytes: 编码后的字节串。
"""
self.data = data.encode(self.characterSet)
return self.data
def fromBase64(self, data):
"""
从Base64编码的字符串加载数据并将其解码为字节串。
参数:
data (str): Base64编码的字符串数据。
返回:
bytes: 解码后的字节串。
"""
self.data = base64.b64decode(data.encode(self.characterSet))
return self.data
def fromHexStr(self, data):
"""
从十六进制字符串加载数据,并将其转换为字节串。
参数:
data (str): 十六进制字符串数据。
返回:
bytes: 转换后的字节串。
"""
self.data = binascii.a2b_hex(data)
return self.data
def toString(self):
"""
将字节串数据转换为字符串。
返回:
str: 转换后的字符串。
"""
return self.data.decode(self.characterSet)
def toBase64(self):
"""
将字节串数据转换为Base64编码的字符串。
返回:
str: 转换后的Base64编码的字符串。
"""
return base64.b64encode(self.data).decode()
def toHexStr(self):
"""
将字节串数据转换为十六进制字符串。
返回:
str: 转换后的十六进制字符串。
"""
return binascii.b2a_hex(self.data).decode()
def toBytes(self):
"""
获取字节串数据。
返回:
bytes: 数据的字节串形式。
"""
return self.data
def __str__(self):
"""
返回数据的字符串表示形式。
如果数据不能直接转换为字符串则返回其Base64编码的字符串形式。
返回:
str: 数据的字符串或Base64编码的字符串形式。
"""
try:
return self.toString()
except Exception:
return self.toBase64()
### 封装类
class AEScryptor():
def __init__(self, key, mode, iv='', paddingMode="NoPadding", characterSet="utf-8"):
'''
构建一个AES对象
key: 秘钥,字节型数据
mode: 使用模式只提供两种AES.MODE_CBC, AES.MODE_ECB
iv iv偏移量字节型数据
paddingMode: 填充模式默认为NoPadding, 可选NoPaddingZeroPaddingPKCS5PaddingPKCS7Padding
characterSet: 字符集编码
'''
self.key = key
self.mode = mode
self.iv = iv
self.characterSet = characterSet
self.paddingMode = paddingMode
self.data = ""
def __ZeroPadding(self, data):
data += b'\x00'
while len(data) % 16 != 0:
data += b'\x00'
return data
def __StripZeroPadding(self, data):
data = data[:-1]
while len(data) % 16 != 0:
data = data.rstrip(b'\x00')
if data[-1] != b"\x00":
break
return data
def __PKCS5_7Padding(self, data):
needSize = 16 - len(data) % 16
if needSize == 0:
needSize = 16
return data + needSize.to_bytes(1, 'little') * needSize
def __StripPKCS5_7Padding(self, data):
paddingSize = data[-1]
return data.rstrip(paddingSize.to_bytes(1, 'little'))
def __paddingData(self, data):
if self.paddingMode == "NoPadding":
if len(data) % 16 == 0:
return data
else:
return self.__ZeroPadding(data)
elif self.paddingMode == "ZeroPadding":
return self.__ZeroPadding(data)
elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding":
return self.__PKCS5_7Padding(data)
else:
print("不支持Padding")
def __stripPaddingData(self, data):
if self.paddingMode == "NoPadding":
return self.__StripZeroPadding(data)
elif self.paddingMode == "ZeroPadding":
return self.__StripZeroPadding(data)
elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding":
return self.__StripPKCS5_7Padding(data)
else:
print("不支持Padding")
def setCharacterSet(self, characterSet):
'''
设置字符集编码
characterSet: 字符集编码
'''
self.characterSet = characterSet
def setPaddingMode(self, mode):
'''
设置填充模式
mode: 可选NoPaddingZeroPaddingPKCS5PaddingPKCS7Padding
'''
self.paddingMode = mode
def decryptFromBase64(self, entext):
'''
从base64编码字符串编码进行AES解密
entext: 数据类型str
'''
mData = MData(characterSet=self.characterSet)
self.data = mData.fromBase64(entext)
return self.__decrypt()
def decryptFromHexStr(self, entext):
'''
从hexstr编码字符串编码进行AES解密
entext: 数据类型str
'''
mData = MData(characterSet=self.characterSet)
self.data = mData.fromHexStr(entext)
return self.__decrypt()
def decryptFromString(self, entext):
'''
从字符串进行AES解密
entext: 数据类型str
'''
mData = MData(characterSet=self.characterSet)
self.data = mData.fromString(entext)
return self.__decrypt()
def decryptFromBytes(self, entext):
'''
从二进制进行AES解密
entext: 数据类型bytes
'''
self.data = entext
return self.__decrypt()
def encryptFromString(self, data):
'''
对字符串进行AES加密
data: 待加密字符串数据类型为str
'''
self.data = data.encode(self.characterSet)
return self.__encrypt()
def __encrypt(self):
if self.mode == AES.MODE_CBC:
aes = AES.new(self.key, self.mode, self.iv)
elif self.mode == AES.MODE_ECB:
aes = AES.new(self.key, self.mode)
else:
print("不支持这种模式")
return
data = self.__paddingData(self.data)
enData = aes.encrypt(data)
return MData(enData)
def __decrypt(self):
if self.mode == AES.MODE_CBC:
aes = AES.new(self.key, self.mode, self.iv)
elif self.mode == AES.MODE_ECB:
aes = AES.new(self.key, self.mode)
else:
print("不支持这种模式")
return
data = aes.decrypt(self.data)
mData = MData(self.__stripPaddingData(data), characterSet=self.characterSet)
return mData