AES-CBC加密生成十六进制密文每日变动,如何实现静态固定加密结果?
问题根因
你当前遇到的相同明文加密结果不一致的问题,主要有几个核心原因:
- AES-CBC是带状态的链式加密模式:如果你重复使用同一个
AES.new生成的cipher对象进行多次加密,加密完第一段明文后,cipher内部会自动把上一段密文的最后一个块作为下一次加密的IV,导致后续加密结果发生变化。 - 自定义空格填充逻辑存在隐患:你当前用
rjust左侧补空格的填充方式,一是如果明文本身开头带空格,解密时的lstrip会误删原始内容;二是如果明文读取时存在隐藏的空白字符、换行符,会直接导致待加密的输入实际不一致,最终密文变动。 - 部分旧版本PyCrypto库存在传入字符串类型密钥/IV的编码兼容问题,也可能导致状态异常。
解决方案
按以下规则修改代码即可保证相同明文每次加密结果完全一致:
- 每次加密新的明文前,都重新初始化独立的AES cipher实例,固定使用相同的密钥和IV
- 替换自定义填充为标准的PKCS7填充,避免空白字符带来的未知问题
- 加密前统一处理明文,确认每次输入的明文完全相同
修改后的完整代码
from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import pandas as pd import mysql.connector # 固定密钥和IV,统一转为bytes类型避免编码差异 FIXED_KEY = b'This is a key123' FIXED_IV = b'This is an IV456' AES_BLOCK_SIZE = 16 myconn = mysql.connector.connect(host="######", user="##", password="######", database="#######") query = """SELECT * from table """ df = pd.read_sql(query, myconn) def encrypt(url): # 每次加密都重新初始化cipher,保证初始状态一致 cipher = AES.new(FIXED_KEY, AES.MODE_CBC, FIXED_IV) # 明文统一编码为utf-8,再用标准PKCS7填充 plain_bytes = url.encode('utf-8') padded_plain = pad(plain_bytes, AES_BLOCK_SIZE) cipher_bytes = cipher.encrypt(padded_plain) return cipher_bytes.hex() def decrypt(cipher_hex): cipher = AES.new(FIXED_KEY, AES.MODE_CBC, FIXED_IV) cipher_bytes = bytes.fromhex(cipher_hex) padded_plain = cipher.decrypt(cipher_bytes) # 解填充后再解码 original_url = unpad(padded_plain, AES_BLOCK_SIZE).decode('utf-8') return original_url # 测试:同一明文多次加密结果完全一致 message = df['values'][4] eypt1 = encrypt(message) eypt2 = encrypt(message) print(f"两次加密结果是否一致:{eypt1 == eypt2}") print(f"解密结果:{decrypt(eypt1)}")
注意事项
- 如果导入
pad/unpad时报错,卸载旧版本PyCrypto后安装pycryptodome即可:pip uninstall pycrypto && pip install pycryptodome - 固定IV虽然能实现相同明文得到相同密文,但会降低加密安全性,如果你的场景对安全性要求较高,不建议长期使用固定IV,可考虑将密文和对应IV一同存储到数据库中,解密时取出对应IV即可。
- 可在加密前打印
repr(message)校验输入,避免从数据库读取的字段存在隐藏换行、空格、编码差异导致输入不一致。
内容的提问来源于stack exchange,提问作者starlord
相关产品推荐
相关产品推荐

