如何用Argon2生成可用于Fernet加密的32字节密钥?
问题:用明文密码生成Fernet兼容的32字节密钥失败
我的目标
将明文密码生成可用于Fernet加密的32字节令牌。
已尝试操作
>>> from cryptography.fernet import Fernet >>> import argon2 >>> import base64 >>> hash = argon2.hash_password('abc'.encode(), hash_len=32) >>> hash b'$argon2i$v=19$m=65536,t=3,p=4$ee2ZpEZ5Q58HwIT91xQxgw$QrzxXCLBOnuzLgVPoccIPcaeF4mS4uvmrRBFzNXZbxw' >>> Fernet(hash) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3/dist-packages/cryptography/fernet.py", line 34, in __init__ raise ValueError( ValueError: Fernet key must be 32 url-safe base64-encoded bytes.
后续尝试
>>> b64_hash = base64.urlsafe_b64decode(hash) >>> b64_hash b'j\xb8(\x9fh\xaf\xd7\xd9' >>> Fernet(b64_hash) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3/dist-packages/cryptography/fernet.py", line 32, in __init__ key = base64.urlsafe_b64decode(key) File "/usr/lib/python3.10/base64.py", line 133, in urlsafe_b64decode return b64decode(s) File "/usr/lib/python3.10/base64.py", line 87, in b64decode return binascii.a2b_base64(s) binascii.Error: Incorrect padding
错误根源与解决方法
核心问题
- 第一次错误:传入Fernet的是argon2生成的完整格式化哈希字符串——这个字符串包含算法标识、参数、盐和哈希值,完全不符合Fernet要求的「32字节原始数据的url-safe base64编码」格式。
- 第二次错误:对整个argon2哈希做base64解码逻辑完全颠倒:Fernet需要的是把32字节原始密钥编码成base64,而非解码;而且argon2哈希里只有最后一段是哈希值的base64编码,整个字符串无法直接解码。
正确实现步骤
要从明文密码生成Fernet可用的密钥,需分两步:从密码导出32字节原始密钥,再将其编码为url-safe base64格式。
代码示例:
from cryptography.fernet import Fernet import argon2 import base64 # 1. 生成盐(若需要固定密钥可复用同一盐,否则每次生成新盐) salt = argon2.generate_salt() # 2. 从明文密码导出32字节原始密钥(用argon2低级API获取原始哈希值) raw_key = argon2.low_level.hash_secret_raw( secret='abc'.encode(), salt=salt, time_cost=3, memory_cost=65536, parallelism=4, hash_len=32, type=argon2.Type.I ) # 3. 将原始密钥编码为Fernet要求的url-safe base64格式 fernet_key = base64.urlsafe_b64encode(raw_key) # 4. 初始化Fernet并测试 f = Fernet(fernet_key) encrypted = f.encrypt(b"测试消息") print("加密结果:", encrypted) decrypted = f.decrypt(encrypted) print("解密结果:", decrypted.decode())
关键提示
argon2.hash_password返回的是用于密码验证的格式化哈希字符串,不适合直接作为加密密钥;要获取原始32字节密钥,必须使用argon2.low_level.hash_secret_raw。- Fernet的密钥格式是「32字节二进制数据 → url-safe base64编码字符串」,切勿搞反编码/解码顺序。
内容的提问来源于stack exchange,提问作者Sven
相关产品推荐
相关产品推荐

