当 AES-256 遇上 SHA256 —— AES 加密中密钥派生策略对兼容性的影响
一次密钥处理陷阱的排查实录…
当 AES-256 遇上 SHA256 —— AES 加密中密钥派生策略对兼容性的影响
环境: Python 3.11
IDE: PyCharm 2025.1.1.1 (Community Edition)
1 问题发现
今天在测试自研的 AES-256 ECB 实现时,遇到了一个有趣的问题。我的代码逻辑看起来完美无缺:完整的轮函数、正确的填充机制、标准的 S 盒生成,但加密结果却与在线 AES 加解密工具不一致。
我所使用的测试用例如下:
明文:"Hello, AES-256!"
密钥:"thisisa32bytestestkeyforaes256!!"
2 问题定位
经过排查和 LLM 的帮助,发现我的密钥处理部分代码有一个画蛇添足的行为,如下列第四行所示:
1
2
3
4
def derive_key(key_or_password: bytes | str) -> bytes:
if isinstance(key_or_password, str):
key_or_password = key_or_password.encode("utf-8")
return hashlib.sha256(key_or_password).digest() # 始终32字节
我总是对输入密钥进行 SHA256 散列(尽管密钥本身已经满足 32 字节),以确保得到 32 字节的密钥。但大多数标准 AES 实现直接使用提供的密钥字节。
这就是兼容性问题的根源:
因此我的密钥经过 SHA256 散列之后被转变为了不同的 32 字节输入:
hashlib.sha256("thisisa32bytestestkeyforaes256!!").digest() != thisisa32bytestestkeyforaes256!!
3 解决方案
我决定提供一个灵活的解决方案,而不是简单地移除散列功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 添加配置选项
HASH_KEY = True # 默认为True保持向后兼容
def derive_key(key_or_password: bytes | str) -> bytes:
if isinstance(key_or_password, str):
key_or_password = key_or_password.encode("utf-8")
if HASH_KEY:
# 使用SHA256散列得到32字节密钥
return hashlib.sha256(key_or_password).digest()
else:
# 直接使用提供的字节密钥
if len(key_or_password) != 32:
raise ValueError("AES-256要求精确的32字节密钥")
return key_or_password
这样提供了两种使用模式:
- 密码模式 (HASH_KEY=True): 接受任意长度输入,通过散列得到固定长度密钥;
- 密钥模式 (HASH_KEY=False): 要求精确的 32 字节密钥,与标准实现兼容。
经测试,解决方案有效!又是 Debug 成功的一天~
(LLM 真好用啊)
本文由作者按照 CC BY 4.0 进行授权