使用cryptography模块开发密码管理器时遇InvalidToken解密错误求助
密码管理器APM触发cryptography.fernet.InvalidToken错误排查
问题描述
开发一款名为APM的密码管理器时,执行find google命令后触发cryptography.fernet.InvalidToken错误,错误位于APMDatabase类decrypt方法第44行(decrypted = fernet.decrypt(original)),谷歌搜索未找到有效解决方案。
报错回溯
hello, thank you for using APM. enter 'help' for help. enjoy! > find google "google", "google.com", "AntispleneticMoonlighty", "#{6P=,U{;%^AMO+", "number- 1" Traceback (most recent call last): File "D:\CODING\Python\APM.py", line 80, in <module> db.decrypt() File "D:\CODING\Python\APM.py", line 44, in decrypt decrypted = fernet.decrypt(original) File "C:\Users\Anton\AppData\Local\Programs\Python\Python310\lib\site-packages\cryptography\fernet.py", line 85, in decrypt timestamp, data = Fernet._get_unverified_token_data(token) File "C:\Users\Anton\AppData\Local\Programs\Python\Python310\lib\site-packages\cryptography\fernet.py", line 121, in _get_unverified_token_data raise InvalidToken cryptography.fernet.InvalidToken Press any key to continue . . .
精简代码
import PGL from cryptography.fernet import Fernet import os import logging as lg class APMDatabase: def __init__(self, keyfile_path, database_path): global start self.keyfile_path = keyfile_path self.database_path = database_path print(start) def read_key(self): with open(self.keyfile_path, "rb") as keyfile: return keyfile.read() def encrypt(self): key = self.read_key() fernet = Fernet(key) with open(self.database_path, "rb") as file: original = file.read() encrypted = fernet.encrypt(original) with open(self.database_path, "wb") as encfile: encfile.write(encrypted) def decrypt(self): key = self.read_key() fernet = Fernet(key) with open(self.database_path, "rb") as file: original = file.read() decrypted = fernet.decrypt(original) # 错误发生行 with open(self.database_path, "wb") as decfile: decfile.write(decrypted) def find(self, name): db.decrypt() with open(self.database_path, "r") as database: content = database.readlines() for i in content: if name in i: print(i) return i db.encrypt() db = APMDatabase( keyfile_path="D:\\CODING\\Python\\APMKEY.APMKEY", database_path="D:\\CODING\\Python\\APMDatabase.APMDATA", ) while True: db.decrypt() number = len((open("D:\\CODING\\Python\\APMDatabase.APMDATA")).readlines()) db.encrypt() query = input("> ") elif "find" in query: data = query.split(" ")[1] if data == "(all)": try: db.find('"') except: lg.error("404") try: db.find(data) except: lg.error("404")
问题分析与解决建议
核心问题
- 数据库状态不一致:主循环中每次输入前都会执行「解密->读取行数->加密」,而
find方法又会执行「解密->读取内容->加密」。如果中间出现异常导致加密步骤未执行,或者重复加密,会导致数据库内容变为密文的密文,后续解密时触发InvalidToken。 - 全局实例引用错误:
find方法中使用全局db实例调用decrypt/encrypt,而非当前实例的self,可能导致操作对象混乱。 - 文件句柄泄漏:主循环中读取行数时直接
open文件未关闭,可能导致文件锁定或状态异常。
修复步骤
- 修正实例引用:将
find方法中的db.decrypt()和db.encrypt()改为self.decrypt()和self.encrypt():def find(self, name): self.decrypt() with open(self.database_path, "r") as database: content = database.readlines() for i in content: if name in i: print(i) return i self.encrypt() - 优化文件读取逻辑:主循环中用
with语句安全读取行数,避免文件未关闭:while True: db.decrypt() with open("D:\\CODING\\Python\\APMDatabase.APMDATA", "r") as f: number = len(f.readlines()) db.encrypt() query = input("> ") # 后续逻辑... - 验证密钥有效性:确认
APMKEY.APMKEY中的密钥是Fernet格式的有效密钥(URL-safe base64编码的32字节字符串)。可通过以下代码验证:from cryptography.fernet import Fernet with open("D:\\CODING\\Python\\APMKEY.APMKEY", "rb") as f: key = f.read() try: # 检查是否为合法Fernet密钥 Fernet(key) print("密钥有效") except ValueError: print("密钥无效,请重新生成Fernet密钥") - 恢复数据库(若已损坏):如果数据库被重复加密,可尝试手动解密恢复:
from cryptography.fernet import Fernet key = b"替换为你的密钥内容" fernet = Fernet(key) with open("D:\\CODING\\Python\\APMDatabase.APMDATA", "rb") as f: data = f.read() try: decrypted = fernet.decrypt(data) with open("恢复的数据库.txt", "wb") as f: f.write(decrypted) print("数据库恢复成功") except Exception as e: print(f"恢复失败:{e}") - 添加异常处理:在
decrypt方法中添加异常捕获,避免程序直接崩溃并记录日志:def decrypt(self): try: key = self.read_key() fernet = Fernet(key) with open(self.database_path, "rb") as file: original = file.read() decrypted = fernet.decrypt(original) with open(self.database_path, "wb") as decfile: decfile.write(decrypted) except Exception as e: lg.error(f"解密失败: {str(e)}") raise # 可根据需求选择是否抛出异常
内容的提问来源于stack exchange,提问作者Anton Chernyshov
相关产品推荐
相关产品推荐

