Python中AES/CBC/PKCS5填充问题:在线解密结果含乱码
AES-256-CBC加密后在线解密出现乱码的问题排查与解决
问题描述
- 用Python实现AES-256-CBC加密,输入内容为
164386,加密后输出密文:MTIzZWRmcjRAIzkwJjY1JES7OMmdSrtGJX+An1UoVNY= - 使用在线解密工具时,得到的结果为
öÁéáfsÊaׇÒH164386,而非预期的原输入164386
相关参数
- 密钥长度:256位
- 加密模式:CBC
- IV值:
b'123edfr4@#90&65$'(16字节,符合AES块大小要求) - 填充方式:PKCS7
原始加密代码
import base64 from Crypto.Cipher import AES from Crypto.Cipher import AES as cryptoAES from base64 import b64decode from base64 import b64encode class Globals: def __init__(self): self.KEY = b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' self.IV = b'123edfr4@#90&65$' BS = cryptoAES.block_size pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[:-ord(s[len(s)-1:])] class AESCipher: def __init__(self): self.key = b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' self.iv = b'123edfr4@#90&65$' def encrypt(self, data): cipher = AES.new(self.key, AES.MODE_CBC, self.iv) _pad = pad(data).encode('utf-8') response = base64.b64encode(self.iv + cipher.encrypt(_pad)).decode('utf-8') print('+++++++++++',response) def test(): app_id = '164386' response = AESCipher().encrypt(app_id) return response output = test()
问题根源
你的加密代码将IV与加密后的密文拼接在一起,再进行Base64编码,但在线解密工具默认会把你输入的整个Base64解码结果当作「纯密文」处理。当工具用指定的IV解密这个拼接内容时:
- 前16字节(原IV)会被当作第一个密文块,解密后与设置的IV异或,产生乱码
- 后面的真实密文块解密后,与前一个块(原IV)异或,得到正确的原内容
最终结果就会出现「前16字节乱码+正确明文」的情况。
解决方案
方案1:适配在线工具解密(无需修改代码)
- 对加密输出的Base64字符串
MTIzZWRmcjRAIzkwJjY1JES7OMmdSrtGJX+An1UoVNY=进行解码,得到字节串 - 分离出前16字节(原IV)和后面的真实密文
- 将分离出的真实密文重新进行Base64编码,作为在线工具的「密文」输入
- 在在线工具中手动填入IV值:
123edfr4@#90&65$ - 其他参数设置:
- 密钥:填入你的32字节密钥(
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX对应的真实值) - 加密模式:CBC
- 填充方式:PKCS7
- 输出格式:UTF-8
- 密钥:填入你的32字节密钥(
方案2:修改加密代码,输出纯密文Base64(推荐)
如果不需要把IV和密文拼接传输,可以调整加密逻辑,只输出密文的Base64编码,IV单独存储或传递:
import base64 from Crypto.Cipher import AES BS = AES.block_size pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[:-ord(s[len(s)-1:])] class AESCipher: def __init__(self): self.key = b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' self.iv = b'123edfr4@#90&65$' def encrypt(self, data): cipher = AES.new(self.key, AES.MODE_CBC, self.iv) padded_data = pad(data).encode('utf-8') ciphertext = cipher.encrypt(padded_data) # 仅输出密文的Base64编码,IV可单独返回或存储 return base64.b64encode(ciphertext).decode('utf-8') def decrypt(self, ciphertext_b64): ciphertext = base64.b64decode(ciphertext_b64) cipher = AES.new(self.key, AES.MODE_CBC, self.iv) padded_plaintext = cipher.decrypt(ciphertext) return unpad(padded_plaintext.decode('utf-8')) def test(): app_id = '164386' ciphertext = AESCipher().encrypt(app_id) print('加密结果:', ciphertext) # 测试解密 plaintext = AESCipher().decrypt(ciphertext) print('解密结果:', plaintext) return ciphertext output = test()
额外优化点
- 移除了代码中重复导入的
AES和base64模块 - 新增了解密方法,方便本地验证加密结果的正确性
内容的提问来源于stack exchange,提问作者Sarath Chandran
相关产品推荐
相关产品推荐

