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
原因分析
- 密钥不匹配(核心原因):Fernet加密的签名验证机制要求解密必须使用和加密完全相同的密钥。修改主密码后,程序生成新的Fernet密钥,用新密钥解密旧密钥加密的数据时,签名验证失败,触发
InvalidSignature异常,最终表现为无法读取数据、密码库“为空”。原教程未实现主密码变更时的密钥迁移逻辑。 - 代码逻辑冗余: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
相关产品推荐
相关产品推荐

