Kamixitong/app/utils/simple_crypto.py
2025-12-12 11:35:14 +08:00

153 lines
4.4 KiB
Python
Raw 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.

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):
"""
初始化加密器
:param key: 加密密钥,必须从应用配置中获取
"""
if key:
self.key = key.encode() if isinstance(key, str) else key
else:
# 从应用配置获取密钥,生产环境必须设置
key_str = current_app.config.get('AUTH_SECRET_KEY')
if not key_str:
raise ValueError("AUTH_SECRET_KEY未设置生产环境必须设置AUTH_SECRET_KEY环境变量")
self.key = key_str.encode('utf-8')
# 使用 PBKDF2 生成固定长度的密钥,使用随机盐值
# 注意:这里不能使用固定盐值,否则会降低加密强度
from secrets import token_bytes
salt = token_bytes(16) # 随机生成盐值
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
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)