Kamixitong/app/utils/simple_crypto.py

153 lines
4.4 KiB
Python
Raw Normal View History

2025-11-11 21:39:12 +08:00
import hashlib
import base64
import secrets
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from flask import current_app
import os
class SimpleCrypto:
"""简化的加密工具类,使用 cryptography 包"""
def __init__(self, key=None):
"""
初始化加密器
2025-12-12 11:35:14 +08:00
:param key: 加密密钥必须从应用配置中获取
2025-11-11 21:39:12 +08:00
"""
if key:
self.key = key.encode() if isinstance(key, str) else key
else:
2025-12-12 11:35:14 +08:00
# 从应用配置获取密钥,生产环境必须设置
key_str = current_app.config.get('AUTH_SECRET_KEY')
if not key_str:
raise ValueError("AUTH_SECRET_KEY未设置生产环境必须设置AUTH_SECRET_KEY环境变量")
2025-11-11 21:39:12 +08:00
self.key = key_str.encode('utf-8')
2025-12-12 11:35:14 +08:00
# 使用 PBKDF2 生成固定长度的密钥,使用随机盐值
# 注意:这里不能使用固定盐值,否则会降低加密强度
from secrets import token_bytes
salt = token_bytes(16) # 随机生成盐值
2025-11-11 21:39:12 +08:00
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
2025-12-12 11:35:14 +08:00
salt=salt,
2025-11-11 21:39:12 +08:00
iterations=100000,
)
self.fernet_key = base64.urlsafe_b64encode(kdf.derive(self.key))
self.cipher = Fernet(self.fernet_key)
def encrypt(self, data):
"""
加密数据
:param data: 要加密的数据字符串
:return: base64编码的加密结果
"""
try:
if isinstance(data, str):
data = data.encode('utf-8')
encrypted_data = self.cipher.encrypt(data)
result = base64.b64encode(encrypted_data)
return result.decode('utf-8')
except Exception as e:
raise ValueError(f"加密失败: {str(e)}")
def decrypt(self, encrypted_data):
"""
解密数据
:param encrypted_data: base64编码的加密数据
:return: 解密后的原始数据
"""
try:
if isinstance(encrypted_data, str):
encrypted_data = encrypted_data.encode('utf-8')
data = base64.b64decode(encrypted_data)
decrypted_data = self.cipher.decrypt(data)
return decrypted_data.decode('utf-8')
except Exception as e:
raise ValueError(f"解密失败: {str(e)}")
def generate_hash(data, salt=None):
"""
生成哈希值
:param data: 要哈希的数据
:param salt: 盐值如果不提供则随机生成
:return: (哈希值, 盐值) 元组
"""
if salt is None:
salt = secrets.token_hex(16)
# 组合数据和盐值
combined = f"{data}{salt}".encode('utf-8')
# 生成SHA256哈希
hash_obj = hashlib.sha256(combined)
hash_value = hash_obj.hexdigest()
return hash_value, salt
def generate_signature(data, secret_key):
"""
生成签名
:param data: 要签名的数据
:param secret_key: 密钥
:return: 签名
"""
combined = f"{data}{secret_key}".encode('utf-8')
hash_obj = hashlib.sha256(combined)
return hash_obj.hexdigest()
def verify_hash(data, hash_value, salt):
"""
验证哈希值
:param data: 原始数据
:param hash_value: 要验证的哈希值
:param salt: 盐值
:return: 验证结果
"""
computed_hash, _ = generate_hash(data, salt)
return computed_hash == hash_value
def generate_token(length=32):
"""
生成随机令牌
:param length: 令牌长度
:return: 随机令牌字符串
"""
return secrets.token_urlsafe(length)
def generate_uuid():
"""
生成UUID
:return: UUID字符串
"""
import uuid
return str(uuid.uuid4())
def encrypt_password(password, salt=None):
"""
加密密码
:param password: 原始密码
:param salt: 盐值
:return: (加密后的密码, 盐值)
"""
return generate_hash(password, salt)
def verify_password(password, hashed_password, salt):
"""
验证密码
:param password: 原始密码
:param hashed_password: 加密后的密码
:param salt: 盐值
:return: 验证结果
"""
return verify_hash(password, hashed_password, salt)
# 为了兼容性,保留原来的函数名
def AESCipher(key=None):
"""兼容性包装器"""
return SimpleCrypto(key)