优化代码风格

This commit is contained in:
ZZX9599
2025-09-15 18:24:33 +08:00
parent ced84e49bc
commit 3cb83b292e
4 changed files with 67 additions and 88 deletions

View File

@ -1,72 +1,56 @@
import base64
import os
from dotenv import load_dotenv
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
from fastapi import HTTPException
# 加载环境变量(从 .env 文件读取密钥
load_dotenv()
# 硬编码AES密钥32字节AES-256
AES_SECRET_KEY = b"jr1vA6tfWMHOYi6UXw67UuO6fdak2rMa"
AES_BLOCK_SIZE = 16 # AES固定块大小
# 校验密钥长度确保符合AES规范
valid_key_lengths = [16, 24, 32]
if len(AES_SECRET_KEY) not in valid_key_lengths:
raise ValueError(
f"AES密钥长度必须为{valid_key_lengths}字节,当前为{len(AES_SECRET_KEY)}字节"
)
class AESCipher:
"""AES-CBC 对称加密工具类"""
# 从环境变量获取密钥AES-256 需 32 字节密钥AES-128 需 16 字节)
SECRET_KEY = "jr1vA6tfWMHOYi6UXw67UuO6fdak2rMa".encode("utf-8")
# AES 块大小固定为 16 字节
BLOCK_SIZE = 16
def aes_encrypt(plaintext: str) -> dict:
"""AES-CBC模式加密(返回密文+IV均为Base64编码"""
try:
# 生成随机IV16字节
iv = os.urandom(AES_BLOCK_SIZE)
@classmethod
def _validate_key(cls):
"""校验密钥长度AES-256 需 32 字节AES-128 需 16 字节)"""
if len(cls.SECRET_KEY) not in (16, 32):
raise ValueError("AES 密钥长度必须为 16 字节AES-128或 32 字节AES-256")
# 创建加密器
cipher = AES.new(AES_SECRET_KEY, AES.MODE_CBC, iv)
@classmethod
def encrypt(cls, data: dict) -> dict:
"""
加密函数:将字典类型的 data 加密
返回:{encrypted_data: 加密后Base64字符串, iv: 16字节IV的Base64字符串}
"""
cls._validate_key()
# 1. 生成 16 字节随机 IV每次加密都生成新 IV无需保密但需和解密一致
iv = get_random_bytes(cls.BLOCK_SIZE)
# 2. 初始化 AES-CBC 加密器
cipher = AES.new(cls.SECRET_KEY, AES.MODE_CBC, iv)
# 3. 数据序列化字典转JSON字符串→ 编码为字节 → 填充PKCS7
data_str = str(data) # 若需更严谨,可使用 json.dumps(data, ensure_ascii=False)
data_bytes = data_str.encode("utf-8")
padded_data = pad(data_bytes, cls.BLOCK_SIZE, style="pkcs7")
# 4. 加密 → 转为 Base64 字符串(便于接口传输)
encrypted_bytes = cipher.encrypt(padded_data)
encrypted_data = base64.b64encode(encrypted_bytes).decode("utf-8")
iv_b64 = base64.b64encode(iv).decode("utf-8")
# 明文填充并加密
padded_plaintext = pad(plaintext.encode("utf-8"), AES_BLOCK_SIZE)
ciphertext = base64.b64encode(cipher.encrypt(padded_plaintext)).decode("utf-8")
iv_base64 = base64.b64encode(iv).decode("utf-8")
return {
"encrypted_data": encrypted_data,
"iv": iv_b64 # IV 需随密文一起返回,供前端解密
"ciphertext": ciphertext,
"iv": iv_base64,
"algorithm": "AES-CBC"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"AES加密失败{str(e)}") from e
@classmethod
def decrypt(cls, encrypted_data: str, iv_b64: str) -> dict:
"""
解密函数:将加密后的 Base64 字符串解密为字典
参数encrypted_data加密数据、iv_b64加密时的IV
"""
cls._validate_key()
# 1. 解码 Base64IV 和 密文)
iv = base64.b64decode(iv_b64)
encrypted_bytes = base64.b64decode(encrypted_data)
def aes_decrypt(ciphertext: str, iv: str) -> str:
"""AES-CBC模式解密"""
try:
# 解码Base64
ciphertext_bytes = base64.b64decode(ciphertext)
iv_bytes = base64.b64decode(iv)
# 2. 初始化 AES-CBC 解密器
cipher = AES.new(cls.SECRET_KEY, AES.MODE_CBC, iv)
# 创建解密器
cipher = AES.new(AES_SECRET_KEY, AES.MODE_CBC, iv_bytes)
# 3. 解密 → 去除填充 → 解码为字符串 → 转为字典(此处简化,实际可用 json.loads
decrypted_bytes = unpad(cipher.decrypt(encrypted_bytes), cls.BLOCK_SIZE, style="pkcs7")
decrypted_str = decrypted_bytes.decode("utf-8")
return eval(decrypted_str) # 生产环境建议用 json.loads避免 eval 安全风险
# 解密并去填充
decrypted_bytes = unpad(cipher.decrypt(ciphertext_bytes), AES_BLOCK_SIZE)
return decrypted_bytes.decode("utf-8")
except Exception as e:
raise HTTPException(status_code=500, detail=f"AES解密失败{str(e)}") from e