# SSL 증명서 자동화 프로젝트 (9/10): PQC 대응과 미래 확장성 ## 양자 컴퓨터의 위협: Y2Q (Year 2 Quantum) 2024년 8월, NIST는 양자 컴퓨터 저항 암호 표준(FIPS 203, 204, 205)을 발표했다. RSA-2048과 ECDSA-P256은 충분히 강력한 양자 컴퓨터가 등장하면 **수 시간 내 해독** 가능하다. ### 주요 일정 ``` 2024년 8월: NIST PQC 표준 발표 2025년: CA들이 PQC 증명서 테스트 시작 2030년 (추정): 양자 컴퓨터 실용화 2035년: 미국 NSA, 연방 정부 완전 PQC 전환 요구 ``` ### Store Now, Decrypt Later (SNDL) 공격 현재 암호화된 통신을 저장해두고, 양자 컴퓨터가 나오면 과거 데이터를 복호화하는 공격. **지금 암호화한 민감 데이터**가 10년 후에도 가치가 있다면 **지금 PQC로 전환**해야 한다. ## NIST PQC 표준 알고리즘 ### 주요 알고리즘 ``` ┌──────────────────┬───────────────────┬─────────────────────────────┐ │ NIST Standard │ Algorithm │ Use Case │ ├──────────────────┼───────────────────┼─────────────────────────────┤ │ FIPS 203 │ ML-KEM (Kyber) │ 키 교환 (TLS handshake) │ │ FIPS 204 │ ML-DSA (Dilithium)│ 디지털 서명 (증명서) │ │ FIPS 205 │ SLH-DSA (SPHINCS+)│ 서명 (백업용) │ └──────────────────┴───────────────────┴─────────────────────────────┘ ``` ### 증명서에서의 역할 ``` 기존 (RSA/ECDSA): Certificate: Public Key: RSA-2048 또는 ECDSA-P256 Signature: CA의 RSA-4096 서명 PQC (ML-DSA): Certificate: Public Key: ML-DSA-87 (3,293 bytes!) Signature: CA의 ML-DSA-87 서명 (4,595 bytes) ``` **문제점**: PQC 증명서는 **10배 이상 크다** (RSA-2048: 294 bytes vs ML-DSA-87: 3,293 bytes). TLS 핸드셰이크 시 여러 증명서 전송 시 MTU 초과 가능성. ## 하이브리드 증명서 전략 완전한 PQC 전환은 레거시 클라이언트와의 호환성 문제가 있다. **하이브리드 방식**이 현실적이다. ### 하이브리드 X.509 증명서 ``` Hybrid Certificate (Composite Signature): Subject: CN=www.example.com Public Key Algorithm: id-composite-key ├─ RSA-2048 (레거시 호환) └─ ML-DSA-87 (PQC) Signature Algorithm: id-composite-signature ├─ RSA-4096 서명 └─ ML-DSA-87 서명 ``` 검증: **둘 중 하나라도 실패하면 거부**. 양자 컴퓨터가 RSA를 깨도 ML-DSA가 보호하고, ML-DSA에 미발견 취약점이 있어도 RSA가 보호한다. ### OpenSSL 3.x PQC 지원 OpenSSL 3.2+ 에서 liboqs provider를 통해 PQC 지원: ```bash # liboqs 설치 git clone https://github.com/open-quantum-safe/liboqs.git cd liboqs && mkdir build && cd build cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/liboqs .. ninja && sudo ninja install # OpenSSL OQS provider git clone https://github.com/open-quantum-safe/oqs-provider.git cd oqs-provider cmake -S . -B _build -DCMAKE_INSTALL_PREFIX=/opt/oqs-provider cmake --build _build && sudo cmake --install _build ``` ### PQC 증명서 생성 (ML-DSA) ```python # pqc/cert_generator.py from cryptography import x509 from cryptography.x509.oid import NameOID from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa import subprocess import tempfile class PQCCertificateGenerator: """PQC 증명서 생성 (OpenSSL oqs-provider 사용)""" def generate_mldsa_keypair(self, algorithm="mldsa87"): """ML-DSA 키페어 생성""" # OpenSSL CLI로 키 생성 (Python oqs bindings도 가능) with tempfile.NamedTemporaryFile(suffix=".pem", delete=False) as f: private_key_path = f.name subprocess.run([ "openssl", "genpkey", "-algorithm", algorithm, "-provider", "oqsprovider", "-provider", "default", "-out", private_key_path ], check=True) # 개인키 읽기 with open(private_key_path, "rb") as f: private_key_pem = f.read() # 공개키 추출 with tempfile.NamedTemporaryFile(suffix=".pem", delete=False) as f: public_key_path = f.name subprocess.run([ "openssl", "pkey", "-in", private_key_path, "-pubout", "-out", public_key_path, "-provider", "oqsprovider", "-provider", "default" ], check=True) with open(public_key_path, "rb") as f: public_key_pem = f.read() return private_key_pem, public_key_pem def generate_hybrid_csr(self, common_name, private_key_rsa, private_key_mldsa): """하이브리드 CSR 생성 (개념적)""" # 현재 cryptography 라이브러리는 composite key 미지원 # 실제로는 OpenSSL CLI 또는 BoringSSL 사용 # 1. RSA CSR 생성 csr_rsa = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, common_name) ]) ).sign(private_key_rsa, hashes.SHA256()) # 2. ML-DSA CSR 생성 (CLI) # ... (복잡하므로 생략) # 3. Composite CSR (RFC 9629) # ... (표준이 아직 초안 단계) return csr_rsa # 임시로 RSA만 반환 ``` ## 마이그레이션 로드맵 ### Phase 1: 인프라 준비 (2025-2026) ```python # config/pqc_config.py PQC_MIGRATION_PHASES = { "phase1_prep": { "timeline": "2025 Q1 - 2026 Q4", "actions": [ "OpenSSL 3.2+ 업그레이드", "liboqs 설치 및 테스트", "PQC 증명서 발급 PoC", "성능 벤치마크 (증명서 크기, 핸드셰이크 시간)" ] }, "phase2_hybrid": { "timeline": "2027 Q1 - 2029 Q4", "actions": [ "하이브리드 증명서 발급 시작", "레거시 시스템 호환성 테스트", "점진적 롤아웃 (카나리 배포)" ] }, "phase3_pqc_only": { "timeline": "2030 Q1 - 2035 Q4", "actions": [ "순수 PQC 증명서 발급", "레거시 RSA/ECDSA 폐기", "양자 안전 완전 전환" ] } } ``` ### 자동화 시스템의 PQC 대응 **우리 자동화 시스템의 이점**: 알고리즘 변경이 설정 변경만으로 가능 ```python # config.py class CertificateConfig(BaseSettings): # 2025년: 기존 알고리즘 default_algorithm: str = "rsa" rsa_key_size: int = 2048 # 2027년: 하이브리드로 전환 (설정만 변경) # default_algorithm: str = "hybrid-rsa-mldsa" # 2030년: 순수 PQC # default_algorithm: str = "mldsa87" # PQC 알고리즘 매핑 pqc_algorithms = { "mldsa87": { "kem": "mlkem1024", # 키 교환 "signature": "mldsa87", # 서명 "openssl_name": "mldsa87" }, "hybrid-rsa-mldsa": { "classical": "rsa-2048", "pqc": "mldsa87" } } ``` ### 증명서 발급 코드 수정 ```python # acme_client.py 수정 class PQCAwareACMEClient(GlobalSignACMEClient): def __init__(self, config): super().__init__() self.cert_config = config def generate_keypair(self): """설정에 따라 RSA 또는 PQC 키페어 생성""" algorithm = self.cert_config.default_algorithm if algorithm == "rsa": return self._generate_rsa_keypair(self.cert_config.rsa_key_size) elif algorithm == "mldsa87": return self._generate_mldsa_keypair() elif algorithm.startswith("hybrid-"): return self._generate_hybrid_keypair(algorithm) else: raise ValueError(f"Unsupported algorithm: {algorithm}") def _generate_mldsa_keypair(self): """ML-DSA 키페어 생성""" generator = PQCCertificateGenerator() private_key_pem, public_key_pem = generator.generate_mldsa_keypair("mldsa87") return private_key_pem, public_key_pem def _generate_hybrid_keypair(self, algorithm): """하이브리드 키페어 생성""" # RSA 키 rsa_private, rsa_public = self._generate_rsa_keypair(2048) # PQC 키 pqc_private, pqc_public = self._generate_mldsa_keypair() # Composite key 구조 (RFC 9629) return { "type": "composite", "classical": {"algorithm": "rsa-2048", "private_key": rsa_private}, "pqc": {"algorithm": "mldsa87", "private_key": pqc_private} } ``` ## 성능 최적화와 호환성 ### 증명서 크기 문제 PQC 증명서는 매우 크다. TLS 1.3에서는 증명서 압축(RFC 8879)을 사용: ```python # nginx.conf ssl_certificate_compression zlib; # 증명서 압축 활성화 # 압축 효과: # ML-DSA-87 증명서: 8KB → 4KB (50% 압축) ``` ### 레거시 클라이언트 대응 ```nginx # Nginx: 클라이언트 능력에 따라 다른 증명서 제공 map $ssl_preread_alpn_protocols $cert_type { default "rsa"; "~*tls13" "hybrid"; # TLS 1.3 클라이언트는 하이브리드 } server { listen 443 ssl; ssl_certificate $cert_type.crt; ssl_certificate_key $cert_type.key; # ... } ``` ### DB 스키마 확장 ```python # models/certificate.py 수정 class Certificate(Base): # 기존 필드... # PQC 관련 필드 추가 key_algorithm = Column(String(50), default="rsa-2048") # "rsa-2048", "mldsa87", "hybrid-rsa-mldsa" is_pqc_enabled = Column(Boolean, default=False) pqc_migration_phase = Column(String(20)) # "phase1_prep", "phase2_hybrid", "phase3_pqc_only" # 하이브리드 증명서의 경우 여러 키 저장 classical_private_key_encrypted = Column(Text) # RSA pqc_private_key_encrypted = Column(Text) # ML-DSA ``` ## 테스트 환경 구축 ```python # tests/test_pqc.py import pytest from pqc.cert_generator import PQCCertificateGenerator @pytest.mark.pqc def test_mldsa_keypair_generation(): """ML-DSA 키페어 생성 테스트""" generator = PQCCertificateGenerator() private_key, public_key = generator.generate_mldsa_keypair("mldsa87") assert private_key is not None assert b"BEGIN PRIVATE KEY" in private_key assert len(private_key) > 3000 # ML-DSA-87은 매우 큼 @pytest.mark.pqc def test_hybrid_certificate_size(): """하이브리드 증명서 크기 테스트""" # RSA-2048 증명서 rsa_cert_size = 1200 # bytes # 하이브리드 (RSA + ML-DSA-87) hybrid_cert_size = 8500 # bytes # MTU 체크 (Ethernet MTU: 1500 bytes) assert hybrid_cert_size < 16384 # TLS record 최대 크기 print(f"⚠ Hybrid cert size: {hybrid_cert_size} bytes (may require fragmentation)") ``` ## 모니터링: PQC 전환 진행률 ```python # monitoring/pqc_metrics.py from prometheus_client import Gauge pqc_adoption_rate = Gauge( 'pqc_certificate_adoption_rate', 'Percentage of PQC-enabled certificates', ['algorithm'] ) def update_pqc_metrics(): """PQC 채택률 메트릭 업데이트""" total = db.query(Certificate).filter(Certificate.status == "active").count() for algorithm in ["rsa-2048", "mldsa87", "hybrid-rsa-mldsa"]: count = db.query(Certificate).filter( Certificate.status == "active", Certificate.key_algorithm == algorithm ).count() rate = (count / total * 100) if total > 0 else 0 pqc_adoption_rate.labels(algorithm=algorithm).set(rate) # Grafana 쿼리: # PQC 채택률: pqc_certificate_adoption_rate{algorithm=~"mldsa.*|hybrid.*"} ``` ## 다음 단계 v10에서는 전체 시스템을 통합하고 프로덕션 배포 가이드를 제공한다: - 시스템 아키텍처 전체 리뷰 - 고가용성(HA) 구성 - 재해 복구(DR) 계획 - 프로덕션 체크리스트 - 운영 가이드 (troubleshooting, 성능 튜닝) --- **시리즈 목차** 1. SSL 증명서 자동화 필요성과 프로젝트 개요 2. ACME 프로토콜 이해와 동작 원리 3. GlobalSign Atlas ACME 연동 실습 4. 도메인 검증 방식 구현 (HTTP-01, DNS-01) 5. ACME 클라이언트 선정과 커스텀 구현 6. 증명서 관리 DB 설계와 API 구현 7. 증명서 배포 자동화 파이프라인 구축 8. 승인 워크플로우와 거버넌스 구현 9. **[현재글] PQC 대응 설계와 미래 확장성** 10. 전체 시스템 통합과 프로덕션 배포 가이드