You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:27:53