Python密码管理器解密时出现cryptography.fernet.InvalidToken错误
Python密码管理器解密时触发
cryptography.fernet.InvalidToken错误 我基于Fernet实现了一个Python密码管理器,但在检索密码的解密环节遇到了cryptography.fernet.InvalidToken错误,以下是我的代码和报错信息:
from cryptography.fernet import Fernet import argparse import os import secrets import string class PasswordManager: def __init__(self): self.master_key = None def set_master_password(self, master_password): self.master_key = Fernet(Fernet.generate_key()) self.master_password = master_password.encode() def encrypt_password(self, password): return self.master_key.encrypt(password.encode()) def decrypt_password(self, encrypted_password): return self.master_key.decrypt(encrypted_password).decode() def generate_password(self, length=12): characters = string.ascii_letters + string.digits + string.punctuation if length < 8: raise ValueError("Password length must be at least 8 characters.") return ''.join(secrets.choice(characters) for _ in range(length)) def store_password(self, service, password): encrypted_password = self.encrypt_password(password) with open('passwords.txt', 'a') as file: file.write(f"{service}: {encrypted_password}\n") def retrieve_password(self, service): with open('passwords.txt', 'r') as file: for line in file: entry = line.split(': ') if entry[0] == service: encrypted_password = entry[1].encode() return self.decrypt_password(encrypted_password) return None def main(): parser = argparse.ArgumentParser(description="Password Manager") parser.add_argument('--action', choices=['store', 'retrieve', 'generate'], help='Operation to perform', required=True) parser.add_argument('--master-password', help='Master password') parser.add_argument('--service', help='Service name') parser.add_argument('--password', help='Password') parser.add_argument('--length', type=int, default=12, help='Length of generated password') args = parser.parse_args() password_manager = PasswordManager() if args.action == 'store': if not args.master_password or not args.service or not args.password: print("Please provide master password, service, and password to store.") else: password_manager.set_master_password(args.master_password) password_manager.store_password(args.service, args.password) print("Password stored successfully.") elif args.action == 'retrieve': if not args.master_password or not args.service: print("Please provide master password and service to retrieve password.") else: password_manager.set_master_password(args.master_password) password = password_manager.retrieve_password(args.service) if password: print(f"Retrieved password for {args.service}: {password}") else: print(f"No password found for {args.service}.") elif args.action == 'generate': if not args.master_password: print("Please provide master password to generate a password.") else: password_manager.set_master_password(args.master_password) generated_password = password_manager.generate_password(args.length) print(f"Generated Password: {generated_password}") if __name__ == '__main__': main()
报错信息
Traceback (most recent call last): File "/home/valan/Desktop/PasswordManager/passmanager_jsv/passwd_manager.py", line 80, in <module> main() File "/home/valan/Desktop/PasswordManager/passmanager_jsv/passwd_manager.py", line 65, in main password = password_manager.retrieve_password(args.service) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/valan/Desktop/PasswordManager/passmanager_jsv/passwd_manager.py", line 38, in retrieve_password return self.decrypt_password(encrypted_password) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/valan/Desktop/PasswordManager/passmanager_jsv/passwd_manager.py", line 19, in decrypt_password return self.master_key.decrypt(encrypted_password).decode() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/cryptography/fernet.py", line 85, in decrypt timestamp, data = Fernet._get_unverified_token_data(token) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/cryptography/fernet.py", line 121, in _get_unverified_token_data raise InvalidToken cryptography.fernet.InvalidToken
核心问题分析
- 密钥完全不匹配:每次调用
set_master_password都会生成全新的Fernet密钥,完全忽略传入的主密码。存储密码用临时密钥,检索时又生成新密钥,自然无法解密旧内容。 - 换行符干扰:从文件读取加密密码时,
entry[1]包含末尾的换行符\n,直接编码后会导致解密时token格式无效。
修复方案
1. 基于主密码派生固定密钥
Fernet要求使用URL安全的32字节密钥,我们用PBKDF2HMAC算法从主密码派生密钥,同时保存随机盐(salt)确保相同密码能生成相同密钥。
2. 清理文件读取的换行符
读取文件行时用strip()移除末尾的换行符和空白字符。
修复后的完整代码
from cryptography.fernet import Fernet from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend import base64 import argparse import os import secrets import string class PasswordManager: def __init__(self): self.master_key = None self.salt_file = "salt.txt" def _get_or_generate_salt(self): # 获取或生成盐,确保相同主密码能派生相同密钥 if os.path.exists(self.salt_file): with open(self.salt_file, "rb") as f: return f.read() else: salt = secrets.token_bytes(16) with open(self.salt_file, "wb") as f: f.write(salt) return salt def set_master_password(self, master_password): # 从主密码派生Fernet密钥 salt = self._get_or_generate_salt() kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend() ) key = base64.urlsafe_b64encode(kdf.derive(master_password.encode())) self.master_key = Fernet(key) def encrypt_password(self, password): return self.master_key.encrypt(password.encode()) def decrypt_password(self, encrypted_password): return self.master_key.decrypt(encrypted_password).decode() def generate_password(self, length=12): characters = string.ascii_letters + string.digits + string.punctuation if length < 8: raise ValueError("密码长度至少为8位。") return ''.join(secrets.choice(characters) for _ in range(length)) def store_password(self, service, password): encrypted_password = self.encrypt_password(password) # 存储时解码为字符串,避免读取时引入多余字符 with open('passwords.txt', 'a') as file: file.write(f"{service}: {encrypted_password.decode()}\n") def retrieve_password(self, service): with open('passwords.txt', 'r') as file: for line in file: line = line.strip() # 移除换行符和空白 if not line: continue entry = line.split(': ') if len(entry) != 2: continue if entry[0] == service: encrypted_password = entry[1].encode() return self.decrypt_password(encrypted_password) return None def main(): parser = argparse.ArgumentParser(description="密码管理器") parser.add_argument('--action', choices=['store', 'retrieve', 'generate'], help='操作类型', required=True) parser.add_argument('--master-password', help='主密码') parser.add_argument('--service', help='服务名称') parser.add_argument('--password', help='密码') parser.add_argument('--length', type=int, default=12, help='生成密码的长度') args = parser.parse_args() password_manager = PasswordManager() if args.action == 'store': if not args.master_password or not args.service or not args.password: print("请提供主密码、服务名称和要存储的密码。") else: password_manager.set_master_password(args.master_password) password_manager.store_password(args.service, args.password) print("密码存储成功。") elif args.action == 'retrieve': if not args.master_password or not args.service: print("请提供主密码和服务名称以检索密码。") else: password_manager.set_master_password(args.master_password) password = password_manager.retrieve_password(args.service) if password: print(f"检索到{args.service}的密码: {password}") else: print(f"未找到{args.service}的密码。") elif args.action == 'generate': if not args.master_password: print("请提供主密码以生成密码。") else: password_manager.set_master_password(args.master_password) generated_password = password_manager.generate_password(args.length) print(f"生成的密码: {generated_password}") if __name__ == '__main__': main()
关键修复点说明
- 密钥派生逻辑:使用PBKDF2HMAC算法结合主密码和固定盐生成密钥,确保相同主密码始终生成一致的解密密钥。
- 盐存储机制:首次运行生成随机盐并保存,后续读取该盐保证密钥一致性。
- 文件读取优化:用
strip()清理换行符,存储时将加密bytes解码为字符串,避免读取时引入无效字符。 - 本地化提示:将操作提示改为中文,提升使用体验。
内容的提问来源于stack exchange,提问作者sahayaValan
相关产品推荐
相关产品推荐

