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

Python密码库教程报错:cryptography.exceptions.InvalidSignature签名不匹配

密码库修改主密码后解密失败问题排查

问题概述

作为Python初学者,制作密码库时修改主密码后出现异常:密码库显示为空,无法读取原主密码下的加密数据,程序抛出大量错误,核心异常为cryptography.exceptions.InvalidSignature: Signature did not match digest。

核心错误

cryptography.exceptions.InvalidSignature: Signature did not match digest

最终会触发上层封装的cryptography.fernet.InvalidToken异常。

关联代码

解密函数

def decrypt(message: bytes, token: bytes) -> bytes:
    return Fernet(token).decrypt(message)

密码库渲染逻辑(vaultScreen函数片段)

if cursor.fetchall() is not None:
        i = 0
        while True:
            cursor.execute("SELECT * FROM vault")
            array = cursor.fetchall()

            if (len(array) == 0):
                break

            lbl1 = Label(second_frame, text=(decrypt(array[i][1], encryptionKey)))
            lbl1.grid(column=0, row=i + 3)
            lbl2 = Label(second_frame, text=(decrypt(array[i][2], encryptionKey)))
            lbl2.grid(column=1, row=i + 3)
            lbl3 = Label(second_frame, text=(decrypt(array[i][3], encryptionKey)))
            lbl3.grid(column=2, row=i + 3)

            btn = Button(second_frame, text="Delete", command=partial(removeEntry, array[i][0]))
            btn.grid(column=6, row=i + 3, pady=10)

            i = i + 1

            cursor.execute("SELECT * FROM vault")
            if len(cursor.fetchall()) <= i:
                break

完整报错栈

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\fernet.py", line 133, in _verify_signature
    h.verify(data[-32:])
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\hazmat\primitives\hmac.py", line 72, in verify
    ctx.verify(signature)
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\hazmat\backends\openssl\hmac.py", line 85, in verify
    raise InvalidSignature("Signature did not match digest.")
cryptography.exceptions.InvalidSignature: Signature did not match digest.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files\Lib\tkinter\__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "c:\Users\user\desktop\opensesame.py", line 150, in done
    vaultScreen()
  File "c:\Users\user\desktop\opensesame.py", line 327, in vaultScreen
    lbl1 = Label(second_frame, text=(decrypt(array[i][1], encryptionKey)))
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\user\desktop\opensesame.py", line 33, in decrypt
    return Fernet(token).decrypt(message)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\fernet.py", line 90, in decrypt
    return self._decrypt_data(data, timestamp, time_info)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\fernet.py", line 151, in _decrypt_data
    self._verify_signature(data)
  File "C:\Users\user\AppData\Roaming\Python\Python311\site-packages\cryptography\fernet.py", line 135, in _verify_signature
    raise InvalidToken
cryptography.fernet.InvalidToken

原因分析

  1. 密钥不匹配(核心原因):Fernet加密的签名验证机制要求解密必须使用和加密完全相同的密钥。修改主密码后,程序生成新的Fernet密钥,用新密钥解密旧密钥加密的数据时,签名验证失败,触发InvalidSignature异常,最终表现为无法读取数据、密码库“为空”。原教程未实现主密码变更时的密钥迁移逻辑。
  2. 代码逻辑冗余:vaultScreen函数中每次循环都重复查询数据库,多次调用cursor.fetchall(),可能导致数据读取的索引异常,但这是次要问题,核心仍为密钥不匹配。

解决方案

1. 紧急恢复(若记得旧主密码)

使用旧主密码登录,此时密钥匹配可正常读取数据,先备份数据库中的加密数据。

2. 实现主密码变更的密钥迁移流程

在修改主密码的功能中添加以下步骤:

  • 用旧主密码生成旧Fernet密钥
  • 用新主密码生成新Fernet密钥
  • 遍历数据库中所有加密条目,依次用旧密钥解密明文,再用新密钥重新加密
  • 将加密后的新数据更新回数据库,同时保存新密钥(替换旧密钥的存储)

示例核心代码:

def change_master_password(old_password, new_password):
    # 生成旧密钥和新密钥(假设密钥从密码推导,比如用PBKDF2)
    old_key = generate_fernet_key(old_password)
    new_key = generate_fernet_key(new_password)
    
    # 读取所有数据
    cursor.execute("SELECT id, col1, col2, col3 FROM vault")
    entries = cursor.fetchall()
    
    # 迁移每个条目
    for entry_id, col1_enc, col2_enc, col3_enc in entries:
        # 解密
        col1_plain = decrypt(col1_enc, old_key)
        col2_plain = decrypt(col2_enc, old_key)
        col3_plain = decrypt(col3_enc, old_key)
        # 重新加密
        col1_new = Fernet(new_key).encrypt(col1_plain)
        col2_new = Fernet(new_key).encrypt(col2_plain)
        col3_new = Fernet(new_key).encrypt(col3_plain)
        # 更新数据库
        cursor.execute("UPDATE vault SET col1=?, col2=?, col3=? WHERE id=?", 
                      (col1_new, col2_new, col3_new, entry_id))
    conn.commit()
    # 保存新密钥(比如存在本地文件或配置)
    save_key(new_key)

3. 修复vaultScreen的循环逻辑

避免重复查询数据库,一次性获取所有数据后遍历,减少IO操作并避免索引错误:

# 一次性获取所有数据
cursor.execute("SELECT * FROM vault")
array = cursor.fetchall()

if array:
    for i, entry in enumerate(array):
        # 解密并渲染
        lbl1 = Label(second_frame, text=decrypt(entry[1], encryptionKey))
        lbl1.grid(column=0, row=i + 3)
        lbl2 = Label(second_frame, text=decrypt(entry[2], encryptionKey))
        lbl2.grid(column=1, row=i + 3)
        lbl3 = Label(second_frame, text=decrypt(entry[3], encryptionKey))
        lbl3.grid(column=2, row=i + 3)
        
        btn = Button(second_frame, text="Delete", command=partial(removeEntry, entry[0]))
        btn.grid(column=6, row=i + 3, pady=10)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 11:40:37