Fernet解密报InvalidToken,密码管理器加密解密故障排查
密码管理器解密触发
cryptography.fernet.InvalidToken错误排查 问题概述
开发了一款密码管理器,通过encryptAndDump函数将用户密码加密后存入JSON文件,searchAndDecrypt函数负责搜索并解密密码。但执行searchAndDecrypt时触发cryptography.fernet.InvalidToken错误,怀疑是密钥生成/保存环节导致问题。
相关代码
密钥生成代码
key = Fernet.generate_key() with open("key.key", "wb") as key_file: key_file.write(key) k=Fernet(key)
encryptAndDump函数
#function to encrypt user-entered password before dumping it into a JSON file def encryptAndDump(username, password): e_password = password.encode('utf-8') encrypted = k.encrypt(e_password) b64_encrypted = base64.b64encode(encrypted).decode('utf-8') a = {site: [username, b64_encrypted]} with open(dictionary, 'r') as f: password_dictionary = json.load(f) password_dictionary = {} password_dictionary.update(a) with open(dictionary, 'w') as f: json.dump(password_dictionary, f)
searchAndDecrypt函数
#function to search JSON file and decrypt password information def searchAndDecrypt(): r_site = input("Enter the Site Name to retrieve your credentials (Password Manager is case sensitive.): ") with open(dictionary, 'r') as f: password_dictionary = json.load(f) if f.readable() else {} for site, user_pass in password_dictionary.items(): if site == r_site: print("ACCOUNT FOUND") while True: c_password = pwinput.pwinput("Confirm your Password Manager password to decrypt login credentials: ") if c_password == m_password: b64_encrypted_password = user_pass[1] encrypted_password = base64.b64decode(b64_encrypted_password) decrypt_user_pass = k.decrypt(encrypted_password).decode('utf-8') print(decrypt_user_pass) break else: print("Invalid password.") break else: print("Site Account not found.")
报错信息
File "/Users/makaylagalbraith/Documents/Python/PasswordManager.py", line 157, in <module> searchAndDecrypt() File "/Users/makaylagalbraith/Documents/Python/PasswordManager.py", line 91, in searchAndDecrypt decrypt_user_pass = k.decrypt(encrypted_password).decode('utf-8') File "/Users/makaylagalbraith/Library/Python/3.9/lib/python/site-packages/cryptography/fernet.py", line 90, in decrypt return self._decrypt_data(data, timestamp, time_info) File "/Users/makaylagalbraith/Library/Python/3.9/lib/python/site-packages/cryptography/fernet.py", line 151, in _decrypt_data self._verify_signature(data) File "/Users/makaylagalbraith/Library/Python/3.9/lib/python/site-packages/cryptography/fernet.py", line 135, in _verify_signature raise InvalidToken cryptography.fernet.InvalidToken
问题分析与解决
核心原因:密钥不匹配
InvalidToken错误的本质是加密和解密使用的不是同一个Fernet密钥,你的密钥生成代码存在致命问题:每次运行程序都会重新生成新密钥并覆盖key.key文件,导致加密用的密钥和后续解密用的密钥完全不同。
修正密钥加载逻辑
应该先检查密钥文件是否存在,不存在再生成;存在则直接读取复用:
import os from cryptography.fernet import Fernet # 复用已有的密钥,避免每次启动重新生成 if not os.path.exists("key.key"): key = Fernet.generate_key() with open("key.key", "wb") as key_file: key_file.write(key) else: with open("key.key", "rb") as key_file: key = key_file.read() k = Fernet(key)
次要问题:加密存储逻辑bug
encryptAndDump函数中,读取JSON后直接执行password_dictionary = {}清空了原有数据,导致每次存储新密码都会覆盖之前的所有记录。修正该逻辑:
def encryptAndDump(username, password): e_password = password.encode('utf-8') encrypted = k.encrypt(e_password) # Fernet加密结果本身就是base64编码的字节串,无需二次编码 encrypted_str = encrypted.decode('utf-8') new_entry = {site: [username, encrypted_str]} try: with open(dictionary, 'r') as f: password_dictionary = json.load(f) except (FileNotFoundError, json.JSONDecodeError): # 文件不存在或解析失败时初始化空字典 password_dictionary = {} # 直接更新字典,保留原有数据 password_dictionary.update(new_entry) with open(dictionary, 'w') as f: json.dump(password_dictionary, f)
额外优化:移除冗余的base64编码
Fernet加密后的输出已经是URL-safe的base64字节串,无需再手动执行base64.b64encode,直接解码为字符串存入JSON即可,解密时直接编码为字节串传入k.decrypt即可,减少不必要的编码步骤。
内容的提问来源于stack exchange,提问作者Makayla Galbraith
相关产品推荐
相关产品推荐

