51 lines
1.4 KiB
Python
51 lines
1.4 KiB
Python
# 数据库字段加密(AES-256-GCM)。与 app.core.crypto 的接口报文加密相互独立。
|
|
import base64
|
|
import os
|
|
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.core.config import get_settings
|
|
|
|
_DETAIL = "字段加密未配置"
|
|
|
|
|
|
def _fail() -> None:
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=_DETAIL)
|
|
|
|
|
|
def _key() -> bytes:
|
|
raw = "".join(get_settings().field_aes_key.split())
|
|
if not raw:
|
|
_fail()
|
|
try:
|
|
key = base64.b64decode(raw, validate=True)
|
|
except Exception:
|
|
_fail()
|
|
if len(key) != 32:
|
|
_fail()
|
|
return key
|
|
|
|
|
|
def encrypt_text(plain: str) -> str:
|
|
if plain == "":
|
|
return ""
|
|
nonce = os.urandom(12)
|
|
ciphertext = AESGCM(_key()).encrypt(nonce, plain.encode("utf-8"), None)
|
|
return base64.b64encode(nonce + ciphertext).decode("ascii")
|
|
|
|
|
|
def decrypt_text(token: str) -> str:
|
|
if token == "":
|
|
return ""
|
|
try:
|
|
raw = base64.b64decode(token, validate=True)
|
|
if len(raw) < 13:
|
|
raise ValueError("short")
|
|
plain = AESGCM(_key()).decrypt(raw[:12], raw[12:], None)
|
|
return plain.decode("utf-8")
|
|
except HTTPException:
|
|
raise
|
|
except Exception:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="字段密文无法解密") from None
|