guardia-itsm/core/crypto.py
2026-06-07 21:13:38 +09:00

55 lines
2.2 KiB
Python

"""
GUARDiA ITSM — 자격증명 암호화 공통 유틸리티
외부 연동 자격증명(API 키·시크릿·토큰·SNMP 커뮤니티 문자열 등)을 DB에 평문으로
저장하지 않기 위한 AES-256-GCM 암호화/복호화 모듈. 여러 라우터(jira_sync·multicloud·
ncloud·public_api_hub·snmp_discovery·sso_provider·upstage_ocr 등)가 공유한다.
키 소스: GUARDIA_ENC_KEY 환경변수 (문자열을 UTF-8 인코딩 후 32바이트로 패딩/절단).
core/seed.py · routers/citizen_portal.py 와 동일한 컨벤션 — 운영 배포 시
반드시 32자 이상의 고유 값으로 설정할 것 (미설정 시 데모 기본값 사용).
저장 형식: base64(nonce[12] + ciphertext + GCM tag[16])
"""
from __future__ import annotations
import base64
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
def _enc_key() -> bytes:
raw = os.environ.get("GUARDIA_ENC_KEY", "guardia-demo-key-32bytes-padding!").encode("utf-8")
return raw[:32].ljust(32, b"\x00")
def encrypt_secret(plain: str | None) -> str | None:
"""평문 자격증명을 AES-256-GCM으로 암호화하여 base64 문자열로 반환한다."""
if not plain:
return plain
nonce = os.urandom(12)
ct = AESGCM(_enc_key()).encrypt(nonce, plain.encode("utf-8"), None)
return base64.b64encode(nonce + ct).decode("ascii")
def decrypt_secret(enc_value: str | None) -> str | None:
"""encrypt_secret()으로 암호화된 문자열을 복호화한다.
평문으로 저장된 레거시 값(이번 암호화 적용 이전 데이터)이나 손상된 값을 만나면
예외를 전파하지 않고 입력값을 그대로 반환한다 — Fail-Safe 원칙(CLAUDE.md)에 따라
기존 연동이 끊기지 않도록 한다.
"""
if not enc_value:
return enc_value
try:
raw = base64.b64decode(enc_value, validate=True)
nonce, ct = raw[:12], raw[12:]
return AESGCM(_enc_key()).decrypt(nonce, ct, None).decode("utf-8")
except Exception:
return enc_value
# kubernetes.py 등에서 참조하는 이름과 호환되는 별칭
encrypt_password = encrypt_secret
decrypt_password = decrypt_secret