44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
import base64
|
|
import json
|
|
import os
|
|
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.core.config import get_settings
|
|
|
|
|
|
def _key() -> bytes:
|
|
settings = get_settings()
|
|
raw = settings.api_payload_aes_key.strip()
|
|
if not raw:
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="接口加密未配置")
|
|
try:
|
|
key = base64.b64decode(raw)
|
|
except Exception:
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="接口加密未配置")
|
|
if len(key) not in {16, 24, 32}:
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="接口加密未配置")
|
|
return key
|
|
|
|
|
|
def encrypt_json(payload: dict | list) -> dict:
|
|
nonce = os.urandom(12)
|
|
plain = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode()
|
|
ciphertext = AESGCM(_key()).encrypt(nonce, plain, None)
|
|
return {"data": base64.b64encode(nonce + ciphertext).decode()}
|
|
|
|
|
|
def decrypt_json(envelope: dict) -> dict | list:
|
|
token = envelope.get("data")
|
|
if not isinstance(token, str):
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="加密报文格式不正确")
|
|
try:
|
|
raw = base64.b64decode(token)
|
|
plain = AESGCM(_key()).decrypt(raw[:12], raw[12:], None)
|
|
return json.loads(plain)
|
|
except HTTPException:
|
|
raise
|
|
except Exception:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="加密报文无法解密")
|