Files
zsglpt-pc/utils/crypto.py
237899745 83fef6dff2 feat: 知识管理平台精简版 - PyQt6桌面应用
主要功能:
- 账号管理:添加/编辑/删除账号,测试登录
- 浏览任务:批量浏览应读/选读内容并标记已读
- 截图管理:wkhtmltoimage截图,查看历史
- 金山文档:扫码登录/微信快捷登录,自动上传截图

技术栈:
- PyQt6 GUI框架
- Playwright 浏览器自动化
- SQLite 本地数据存储
- wkhtmltoimage 网页截图

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 22:16:36 +08:00

157 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
加密工具模块 - 精简版
用于加密存储敏感信息(如第三方账号密码)
使用Fernet对称加密
"""
import os
import base64
from pathlib import Path
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def _get_key_paths():
"""获取密钥文件路径"""
from config import ENCRYPTION_KEY_FILE, ENCRYPTION_SALT_FILE
return ENCRYPTION_KEY_FILE, ENCRYPTION_SALT_FILE
def _get_or_create_salt() -> bytes:
"""获取或创建盐值"""
_, salt_path = _get_key_paths()
if salt_path.exists():
with open(salt_path, 'rb') as f:
return f.read()
# 生成新的盐值
salt = os.urandom(16)
salt_path.parent.mkdir(parents=True, exist_ok=True)
with open(salt_path, 'wb') as f:
f.write(salt)
return salt
def _derive_key(password: bytes, salt: bytes) -> bytes:
"""从密码派生加密密钥"""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000, # OWASP推荐的迭代次数
)
return base64.urlsafe_b64encode(kdf.derive(password))
def get_encryption_key() -> bytes:
"""获取加密密钥(优先环境变量,否则从文件读取或生成)"""
key_path, _ = _get_key_paths()
# 优先从环境变量读取
env_key = os.environ.get('ENCRYPTION_KEY')
if env_key:
salt = _get_or_create_salt()
return _derive_key(env_key.encode(), salt)
# 从文件读取
if key_path.exists():
with open(key_path, 'rb') as f:
return f.read()
# 生成新的密钥
key = Fernet.generate_key()
key_path.parent.mkdir(parents=True, exist_ok=True)
with open(key_path, 'wb') as f:
f.write(key)
print(f"[OK] 已生成新的加密密钥")
return key
# 全局Fernet实例
_fernet = None
def _get_fernet() -> Fernet:
"""获取Fernet加密器懒加载"""
global _fernet
if _fernet is None:
key = get_encryption_key()
_fernet = Fernet(key)
return _fernet
def encrypt_password(plain_password: str) -> str:
"""
加密密码
Args:
plain_password: 明文密码
Returns:
str: 加密后的密码base64编码
"""
if not plain_password:
return ''
fernet = _get_fernet()
encrypted = fernet.encrypt(plain_password.encode('utf-8'))
return encrypted.decode('utf-8')
def decrypt_password(encrypted_password: str) -> str:
"""
解密密码
Args:
encrypted_password: 加密的密码
Returns:
str: 明文密码
"""
if not encrypted_password:
return ''
try:
fernet = _get_fernet()
decrypted = fernet.decrypt(encrypted_password.encode('utf-8'))
return decrypted.decode('utf-8')
except Exception as e:
# 解密失败,可能是旧的明文密码
print(f"[Warning] 密码解密失败,可能是未加密的旧数据: {e}")
return encrypted_password
def is_encrypted(password: str) -> bool:
"""
检查密码是否已加密
Fernet加密的数据以'gAAAAA'开头
Args:
password: 要检查的密码
Returns:
bool: 是否已加密
"""
if not password:
return False
return password.startswith('gAAAAA')
def migrate_password(password: str) -> str:
"""
迁移密码:如果是明文则加密,如果已加密则保持不变
Args:
password: 密码(可能是明文或已加密)
Returns:
str: 加密后的密码
"""
if is_encrypted(password):
return password
return encrypt_password(password)