【Python】Crypto模块_ AES加解密(MODE CBC/ECB/OFB )
高级加密标准AES的工作模式
mode对象: MODE_ECB、 MODE_CBC、MODE_CFB、MODE_OFB
ECB模式(电子密码本模式:Electronic codebook) ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。 CBC模式(密码分组链接:Cipher-block chaining) CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或 CBC加密:https://www.cnblogs.com/phoenixy/p/15793339.html CTR模式(计数器模式) CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。自增的算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。 这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。 CFB模式(密文反馈:Cipher feedback) 与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher) OFB模式(输出反馈:Output feedback) OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。
AES.MODE_CBC 加解密实现
# -*- coding: utf-8 -*- import base64 from Crypto.Cipher import AES from loguru import logger as logs class aes_cbc_tools: """aes cbc模式加解密工具""" def __init__(self): # aes工作模式 mode对象, MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB self.mode = AES.MODE_CBC # 秘钥和偏移量 16位 # self.key = b"123456789abcdefg" # self.iv = b"abcdefg123456789" self.key = base64.b64decode(config.get("key_iv", "aes_mode_cbc_key")) self.iv = base64.b64decode(config.get("key_iv", "aes_mode_cbc_iv")) def aes_cbc_encrypt(self, text): """ AES/CBC/PKCS5Padding 加密 :param text: 需要加密的数据 :return: 加密后的数据 """ # 补位 BLOCK_SIZE = AES.block_size text = text + (BLOCK_SIZE - len(text.encode()) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(text.encode()) % BLOCK_SIZE) # 创建AES对象 cipher = AES.new(key=self.key, mode=self.mode, IV=self.iv) # 利用AES对象 对数据进行加密 encrypted_text = cipher.encrypt(text.encode()) # 数据base64加密 url解码 utf-8 return base64.b64encode(encrypted_text).decode() def aes_cbc_decrypt(self, encrypted_text): """ AES/CBC/PKCS5Padding 解密 :param encrypted_text: 需要解密的数据 :return: 解密后的数据 """ # 加密数据base64解密 encrypted_text = base64.b64decode(encrypted_text) # AES 秘钥和偏移量 16位 key = self.key iv = self.iv # 创建AES对象 cipher = AES.new(key=self.key, mode=self.mode, IV=self.iv) # 使用AES对象对加密数据进行解密 decrypted_text = cipher.decrypt(encrypted_text) # 去除补位 dec_res = decrypted_text[:- ord(decrypted_text[len(decrypted_text) - 1:])] # 返回解码数据 return dec_res.decode() if __name__ == "__main__": text = "假面舞会" en_text = aes_cbc_tools().aes_cbc_encrypt(text) de_text = aes_cbc_tools().aes_cbc_decrypt(en_text) print(en_text) print(de_text)
执行结果:
AEX.MODE_ECB加解密实现
# -*- coding: utf-8 -*- import base64 import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import pad from loguru import logger as logs class aes_ecb_tools: """aes_ecb 加解密工具""" def __init__(self): """ 用于加密或解密的初始化aes对象。 """ # 补位 self.BS = AES.block_size # aes工作模式 mode对象, MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB self.mode = AES.MODE_ECB # self.key = "12345" # self.key = config.get("key_iv", "aes_mode_ecb_key") self.key = self.get_key()
# self.aes = AES.new(self.get_sha1prng_key(self.key), self.mode) # AES SHA1PRNG 算法加解密 self.aes = AES.new(self.add_to_16(self.key), self.mode) # key不足16位时补位 @staticmethod def get_key(n: int = 16): """ 获取密钥 n 密钥长度 :return: """ c_length = int(n) source = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' length = len(source) - 1 result = '' for i in range(c_length): result += source[random.randint(0, length)] return result @staticmethod def get_sha1prng_key(key: str) -> bytes: """ 使用SHA1PRNG加密密钥。(将十进制转换为十六进制) :param string key: 原始秘钥 :return bytes: 使用SHA1PRNG、128位或16个长字节加密密钥 """ signature: bytes = hashlib.sha1(key.encode()).digest() signature: bytes = hashlib.sha1(signature).digest() return signature[:16] @staticmethod def add_to_16(value): """ 需要补位,str不是16的倍数那就补足为16的倍数 """ while len(value) % 16 != 0: value += '\0' # 不够16位时用0补齐 return str.encode(value) # 返回bytes @staticmethod def padding(s: str) -> str: """填充 PKCS5""" pad_num: int = 16 - len(s) % 16 return s + pad_num * chr(pad_num) @staticmethod def unpadding(s): """去掉填充 PKCS5""" print("s[-1]=", s[-1]) padding_num: int = 16 - len(s) % 16 padding_num: int = ord(s[padding_num]) return s[: -padding_num] @staticmethod def padding_zero(s): """ 字符串处理,过滤特殊字符 """ lists = [] for c in s: # ascii码范围获取 if ord(c) > 31 & ord(c) < 127: lists.append(c) def encrypt_to_bytes(self, content_str): """从字符串加密到字节密文""" self.pad = lambda s: s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS) content_bytes = pad(content_str.encode("utf8"), 16) # print('content_bytes=',content_bytes) ciphertext_bytes = self.aes.encrypt(content_bytes) return ciphertext_bytes def encrypt_to_base64(self, content_str): """从字符串加密到base64密文""" ciphertext_bytes = self.encrypt_to_bytes(content_str) ciphertext_bs64 = base64.b64encode(ciphertext_bytes).decode() return ciphertext_bs64 def decrypt_from_bytes(self, ciphertext_bytes): """从字节密文解密到字符串""" unpad = lambda s: s[:-ord(s[len(s) - 1:])] content_bytes = self.aes.decrypt(ciphertext_bytes) meg = unpad(content_bytes).decode("Utf-8") return meg def decrypt_from_base64(self, ciphertext_bs64): """从base64密文解密到字符串""" ciphertext_bytes = base64.b64decode(ciphertext_bs64) content_str = self.decrypt_from_bytes(ciphertext_bytes) return content_str
if __name__ == "__main__": text = "{'name':'zhangsan', 'age':'18'}" en_txt = aes_ecb_tools().encrypt_to_base64(text) print(en_txt) de_txt = aes_ecb_tools().decrypt_from_base64(en_txt) print(de_txt)
执行结果
AES.MODE_OFB 加解密实现
# -*- coding:utf-8 -*- from Crypto.Cipher import AES from binascii import b2a_hex, a2b_hex from xx.logger import * class aes_key(): # 加解密钥 长度一般为:16, 24, 32 key = 'aes_keysaes_keysaes_keys' # aes工作模式 mode对象, MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB mode = AES.MODE_OFB def use_aes_encrypto(self, message): # 实例化一个对象 cryptor cryptor = AES.new(self.key.encode('utf-8'), self.mode, b'0000000000000000') # 对要加密的信息先进行处理; MODE_OFB 信息长度要处理成16的倍数 length = 16 count = len(message) if count % length != 0: add = length - (count % length) else: add = 0 message = message + ('\0' * add) # 法对信息进行加密 ciphertext = cryptor.encrypt(message.encode('utf-8')) # 对加密结果进行16进制处理。 result = b2a_hex(ciphertext) logs.debug(result.decode('utf-8')) return result def use_aes_decrypto(self, enc_re): cryptor = AES.new(self.key.encode('utf-8'), self.mode, b'0000000000000000') # a2b_hex 对密文进行16进制处理,然后进行 decrypto解密 plain_text = cryptor.decrypt(a2b_hex(enc_re)) logs.debug(plain_text.decode('utf-8').rstrip('\0')) if __name__ == "__main__": message = "需要加密的信息123" # en_message = "6b6f8ad38dcbc28e65c2cd0360780f8c5070af38d0c3942a8e242a1de74e" en_message = aes_key().use_aes_encrypto(message) aes_key().use_aes_decrypto(en_message)
执行结果
AES加解密MODE_CBC和MODE_ECB两种模式的完整实现
import json from Crypto.Cipher import AES import base64 import binascii # 数据类 class MData(): def __init__(self, data=b"", characterSet='utf-8'): # data肯定为bytes self.data = data self.characterSet = characterSet def saveData(self, FileName): with open(FileName, 'wb') as f: f.write(self.data) def fromString(self, data): self.data = data.encode(self.characterSet) return self.data def fromBase64(self, data): self.data = base64.b64decode(data.encode(self.characterSet)) return self.data def fromHexStr(self, data): self.data = binascii.a2b_hex(data) return self.data def toString(self): return self.data.decode(self.characterSet) def toBase64(self): return base64.b64encode(self.data).decode() def toHexStr(self): return binascii.b2a_hex(self.data).decode() def toBytes(self): return self.data def __str__(self): try: return self.toString() except Exception: return self.toBase64() # 封装类 class AEScryptor(): def __init__(self, key, mode, iv='', paddingMode="NoPadding", characterSet="utf-8"): """ 构建一个AES对象 :param key: 秘钥,字节型数据 :param mode: 使用模式,只提供两种,AES.MODE_CBC, AES.MODE_ECB :param iv: iv偏移量,字节型数据 :param paddingMode: 填充模式,默认为NoPadding, 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding :param characterSet: 字符集编码 """ self.key = key self.mode = mode self.iv = iv self.characterSet = characterSet self.paddingMode = paddingMode self.data = "" def __ZeroPadding(self, data): """ 用b’\x00’进行填充,这里的0可不是字符串0,而是字节型数据的b’\x00’ :param data: :return: """ 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): """ 设置字符集编码 :param characterSet: 字符集编码 :return: """ self.characterSet = characterSet def setPaddingMode(self, mode): """ 设置填充模式 :param mode: 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding :return: """ self.paddingMode = mode def decryptFromBase64(self, entext): """ 从base64编码字符串编码进行AES解密 :param entext: 数据类型str :return: """ mData = MData(characterSet=self.characterSet) self.data = mData.fromBase64(entext) return self.__decrypt() def decryptFromHexStr(self, entext): """ 从hexstr编码字符串编码进行AES解密 :param entext:数据类型str :return: """ mData = MData(characterSet=self.characterSet) self.data = mData.fromHexStr(entext) return self.__decrypt() def decryptFromString(self, entext): """ 从字符串进行AES解密 :param entext: 数据类型str :return: """ mData = MData(characterSet=self.characterSet) self.data = mData.fromString(entext) return self.__decrypt() def decryptFromBytes(self, entext): """ 从二进制进行AES解密 :param entext: 数据类型bytes :return: """ self.data = entext return self.__decrypt() def encryptFromString(self, data): """ 对字符串进行AES加密 :param data: 待加密字符串,数据类型为str :return: """ 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 if __name__ == '__main__': key = b"1234567812345678" iv = b"0000000000000000" aes = AEScryptor(key, AES.MODE_CBC, iv, paddingMode="ZeroPadding", characterSet='utf-8') data = '{"name": "张三", "age": "18", "phone": "13212345678"}' enrData = aes.encryptFromString(data) print("密文:", enrData.toBase64()) denrData = aes.decryptFromBase64(enrData.toBase64()) print("明文:", denrData) print("明文:", type(eval(str(denrData))))
执行结果
源码地址:https://blog.csdn.net/chouzhou9701/article/details/122019967
crypto模块安装参考:https://www.cnblogs.com/phoenixy/p/15741604.html
-------------------------------------------------------------------------------------
如果万事开头难 那请结局一定圆满 @ Phoenixy
-------------------------------------------------------------------------------------