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

使用Fernet和base64在Python实现安全密码管理时遇InvalidToken错误

解决Fernet InvalidToken错误的方案

问题根源

你直接将load_key()获取的密钥与主密码拼接后作为Fernet密钥,这违反了Fernet的核心规则:Fernet要求密钥必须是URL-safe的base64编码32字节数据。直接拼接主密码会导致密钥格式完全失效,加密和解密使用的密钥不匹配,最终触发InvalidToken错误。

修复步骤

  • 用主密码+随机盐的方式,通过哈希算法派生合法的Fernet密钥
  • 保存盐值(解密时需要用相同盐派生密钥)
  • 修正加密解密的逻辑,确保前后密钥一致

修正后的代码

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import os

def generate_and_save_key(master_pwd):
    # 生成16字节随机盐
    salt = os.urandom(16)
    # 初始化密钥派生函数
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=480000,
    )
    # 派生符合Fernet要求的密钥
    key = base64.urlsafe_b64encode(kdf.derive(master_pwd.encode()))
    # 盐+密钥写入文件(前16字节是盐,后面是密钥)
    with open("key.key", "wb") as key_file:
        key_file.write(salt + key)

def load_key(master_pwd):
    with open("key.key", "rb") as key_file:
        data = key_file.read()
    # 拆分盐和密钥
    salt = data[:16]
    stored_key = data[16:]
    # 用相同盐重新派生密钥并验证主密码
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=480000,
    )
    try:
        kdf.verify(master_pwd.encode(), stored_key)
    except Exception:
        raise ValueError("主密码错误")
    return stored_key

master_pwd = input("Enter master password: ")

# 第一次运行时取消注释生成密钥,之后注释该行避免覆盖
# generate_and_save_key(master_pwd)

try:
    key = load_key(master_pwd)
except FileNotFoundError:
    print("未找到密钥文件,请先生成密钥")
    exit()

fer = Fernet(key)

def view():
    try:
        with open("passwords.txt", 'r') as f:
            for line in f.readlines():
                data = line.rstrip()
                if not data:
                    continue
                user, passw = data.split("|")
                decrypted_pwd = fer.decrypt(passw.encode()).decode()
                print(f"Username: {user} | Password: {decrypted_pwd}")
    except FileNotFoundError:
        print("暂无保存的密码")

def create():
    name = input("username: ")
    pwd = input("password: ")
    encrypted_pwd = fer.encrypt(pwd.encode()).decode()
    with open("passwords.txt", 'a') as f:
        f.write(f"{name}|{encrypted_pwd}\n")
    print("密码已保存")

while True:
    mode = input("Create a new password or view existing ones (view, create), press x to quit? ").lower()
    if mode == "x":
        break
    if mode == "view":
        view()
    elif mode == "create":
        create()
    else:
        print("无效指令,请重新输入")
        continue

额外提示

  • 首次运行必须取消注释generate_and_save_key(master_pwd)生成密钥文件,后续运行要注释该行,防止密钥被覆盖导致旧密码无法解密
  • 新增了主密码验证逻辑,输入错误密码会直接报错,避免后续解密失败
  • 处理了文件不存在的情况,提升程序容错性

内容的提问来源于stack exchange,提问作者victor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:05:01