Tkinter应用用户凭据加密存储与解密调用解决方案咨询
解决Fernet重复加密旧数据的问题
核心问题分析
重复加密旧数据的本质是读取已加密的JSON数据时未解密,直接将加密字符串当作明文再次加密,或是密钥未持久化导致旧数据无法解密(常被误判为重复加密)。
解决方案
1. 密钥持久化管理
必须固定密钥,不能每次启动应用生成新密钥,否则旧加密数据将永久无法解密。
from cryptography.fernet import Fernet import json import os # 密钥存储路径,建议放在非公开目录 KEY_PATH = "cred_secret.key" def get_fernet(): # 加载已有密钥,不存在则生成并保存 if os.path.exists(KEY_PATH): with open(KEY_PATH, "rb") as f: key = f.read() else: key = Fernet.generate_key() with open(KEY_PATH, "wb") as f: f.write(key) return Fernet(key) fernet = get_fernet()
2. 数据读写流程规范
严格遵循读取时解密所有加密字段,写入时仅加密明文字段的逻辑:
读取凭据(解密)
def load_credentials(): CRED_FILE = "credentials.json" if not os.path.exists(CRED_FILE): return [] with open(CRED_FILE, "r") as f: encrypted_list = json.load(f) # 解密每个凭据的用户名和密码 decrypted_list = [] for item in encrypted_list: decrypted_list.append({ "id": item["id"], "site": item["site"], "username": fernet.decrypt(item["username"].encode()).decode(), "password": fernet.decrypt(item["password"].encode()).decode() }) return decrypted_list
保存凭据(加密)
def save_credentials(cred_list): CRED_FILE = "credentials.json" encrypted_list = [] for cred in cred_list: encrypted_list.append({ "id": cred["id"], "site": cred["site"], "username": fernet.encrypt(cred["username"].encode()).decode(), "password": fernet.encrypt(cred["password"].encode()).decode() }) with open(CRED_FILE, "w") as f: json.dump(encrypted_list, f, indent=2)
3. 新增/更新凭据逻辑
基于明文数据操作,避免触碰已加密内容:
新增凭据
def add_new_credential(new_cred): # new_cred是明文字典:{"id": 1, "site": "example.com", "username": "user1", "password": "pass123"} all_creds = load_credentials() all_creds.append(new_cred) save_credentials(all_creds)
更新凭据
def update_credential(target_id, updated_cred): # updated_cred是更新后的明文数据 all_creds = load_credentials() for idx, cred in enumerate(all_creds): if cred["id"] == target_id: all_creds[idx] = updated_cred break save_credentials(all_creds)
4. Treeview选择后解密发送
def on_tree_select(event): selected_item = treeview.selection()[0] cred_id = treeview.item(selected_item, "values")[0] # 假设ID是Treeview第一列 all_creds = load_credentials() target_cred = next(c for c in all_creds if c["id"] == cred_id) # 直接使用明文发送到网站 send_to_website(target_cred["username"], target_cred["password"]) # 绑定Treeview选择事件 treeview.bind("<<TreeviewSelect>>", on_tree_select)
常见错误规避
- 禁止读取JSON后直接对加密字段再次加密,必须先解密得到明文
- 密钥不能随应用重启生成,必须持久化存储
- 不要混合存储明文和加密数据,统一在写入前加密、读取后解密
内容的提问来源于stack exchange,提问作者Muhammad Sufyan
相关产品推荐
相关产品推荐

