拓展公司自动化测试业务,某个接口需要用到加解密,业务是用java的AES/CBC/PKCS5Padding加解密,搬了各处砖,都不同的坑,只能在一次次踩坑报错中自力更生,分享出来以便大家别走我这样的路子,还有就是给自己做个标记,以后要用也好找~~~~
哈哈哈!!!
写得不好,请多多指教~
先普及一下AES加密
AES加密
AES加密是建立在DES加密不能满足破解难度而产生的。由比利时两位非常著名的密码学家Joan Daemen和Vincent Rijmen设计,选取了分组长度为128byte,密钥长度为128byte、192byte和byte比特的三个版本。 分组密码有五种工作体制:
- 电码本模式Electronic Codebook Book (ECB)
- 密码分组链接模式Cipher Block Chaining (CBC)
- 计算器模式Counter (CTR)
- 密码反馈模式Cipher FeedBack (CFB)
- 输出反馈模式Output FeedBack (OFB)
CBC模式
优点:
- 不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:
-
不利于并行计算;
-
误差传递;
-
需要初始化向量IV
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #!/usr/bin/python3 # -*- coding: utf-8 -*- # Author : holidaylzz import traceback import base64 import hashlib from Crypto.Cipher import AES from . import IllegalArgumentException from . import logger class AESCrypt: """ AES/CBC/PKCS5Padding 加密 """ def __init__(self, key): self.key = str.encode(key) self.iv = bytes(16) self.MODE = AES.MODE_CBC self.block_size = 16 # self.padding = lambda data: data + (self.block_size - len(data) % self.block_size) * chr(self.block_size - len(data) % self.block_size) # 此处为一坑,需要现将data转换为byte再来做填充,否则中文特殊字符等会报错 # 填充函数 self.padding = lambda data: data + (self.block_size - len(data.encode('utf-8')) % self.block_size) * chr(self.block_size - len(data.encode('utf-8')) % self.block_size) # 截断函数 self.unpadding = lambda data: data[:-ord(data[-1])] def aes_encrypt(self, text): try: if len(self.key) != 16: raise IllegalArgumentException(bytes.decode(self.key), "密钥长度非16位!!!") else: # 填充16位 padding_text = self.padding(text).encode("utf-8") # 初始化加密器 cryptor = AES.new(self.key, self.MODE, self.iv) # 进行AES加密 encrypt_aes = cryptor.encrypt(padding_text) # 进行BASE64转码 encrypt_text = (base64.b64encode(encrypt_aes)).decode() except IllegalArgumentException as obj: logger.error(obj.msg) except Exception: error = traceback.format_exc() logger.error(error) else: return encrypt_text def aes_decrypt(self, text): try: if len(self.key) != 16: raise IllegalArgumentException(bytes.decode(self.key), "密钥长度非16位!!!") else: cryptor = AES.new(self.key, self.MODE, self.iv) # 进行BASE64转码 plain_base64 = base64.b64decode(text) # 进行ASE解密 decrypt_text = cryptor.decrypt(plain_base64) # 截取 plain_text = self.unpadding(decrypt_text.decode("utf-8")) except IllegalArgumentException as obj: logger.error(obj.msg) except UnicodeDecodeError: logger.error("解密失败,请检查密钥是否正确!") error = traceback.format_exc() logger.error(error) except binascii.Error: logger.error("BASE64解码失败!") error = traceback.format_exc() logger.error(error) except Exception: error = traceback.format_exc() logger.error(error) else: return plain_text cryptor = AESCrypt("ZGJfXxZNGPqWAC53") print(cryptor.aes_encrypt("原谅我个渣渣!!!")) print(cryptor.aes_decrypt("KMLeX7zYGc3SBZi55/BR0VnMybZb29CrFJFl3ac8/k=")) |
输出:
1 2 | KMpLeX7zYGc3SBZi55/BR5Eisp18xkYQ4Ib5dAEENEM= 原谅我个渣渣!!! |
踩坑1->>>key,text,iv未转byte报错:TypeError: Object type
1 2 | raise TypeError("Object type %s cannot be passed to C code" % type(data)) TypeError: Object type <class 'str'> cannot be passed to C code |
报错解决:key,text,iv均需要转为byte类型
1 2 3 4 | key = str.encode(key) // key = key.encode("utf-8") 转byte方式任选,随你开心 iv = bytes(16) text = text.encode("utf-8") |
踩坑2->>>中文/特殊字符加密报错:ValueError: Data must be padded to 16 byte boundary in CBC mode
1 2 3 4 | padding = lambda data: data + (block_size - len(data) % block_size) * chr(block_size - len(data) % block_size) raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size) ValueError: Data must be padded to 16 byte boundary in CBC mode |
报错解决:当出现中文或者特殊字符,text先做填充再转字节,用来加密的text字节可能16byte整数倍
way:现将text转为字节,再做填充
1 | .padding = lambda data: data + (block_size - len(data.encode('utf-8')) % block_size) * chr(block_size - len(data.encode('utf-8')) % block_size) |