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