암호화 (Encryption)

핵심 인사이트 (3줄 요약)

평문을 암호문으로 변환하여 기밀성을 보장하는 기술. 대칭키(AES, ChaCha20)는 빠르고 비대칭키(RSA, ECC)는 안전한 키 분배. 실무는 하이브리드 방식(TLS)으로 조합한다.


Ⅰ. 개요 (필수: 200자 이상)

개념: 암호화(Encryption)는 평문(Plaintext)을 수학적 알고리즘과 키를 사용하여 암호문(Ciphertext)으로 변환하여, 권한이 없는 사용자가 내용을 알 수 없게 하는 기밀성 보장 기술이다. 복호화(Decryption)는 반대 과정이다.

💡 비유: 암호화는 "비밀 금고" 같아요. 열쇠(키)가 있어야만 내용물(평문)을 꺼낼 수 있죠. 금고가 아무리 튼튼해도(알고리즘이 좋아도), 열쇠를 잃어버리면(키 유출) 끝이에요!

등장 배경 (필수: 3가지 이상 기술):

  1. 기존 문제점 - 도청 위험: 네트워크로 전송되는 데이터는 누구나 가로챌 수 있음 (Wi-Fi 스니핑, ISP 로깅)
  2. 기술적 필요성 - CIA 3요소: 기밀성(Confidentiality), 무결성(Integrity), 가용성(Availability) 중 기밀성과 무결성 확보
  3. 시장/산업 요구 - 규제 준수: GDPR, 개인정보보호법 등 데이터 보호 의무화. 미암호화 시 과태료 부과

핵심 목적: 기밀성 보장, 무결성 검증, 부인방지, 인증


Ⅱ. 구성 요소 및 핵심 원리 (필수: 가장 상세하게)

암호화 분류 체계 (필수: ASCII 아트):

┌─────────────────────────────────────────────────────────────────────────┐
│                        암호화 기술 분류                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   암호화                                                                 │
│   ├── 양자 암호 (Quantum)                                               │
│   │   └── QKD (Quantum Key Distribution)                               │
│   │                                                                     │
│   └── 고전 암호 (Classical)                                             │
│       ├── 대칭키 (Symmetric)                                           │
│       │   ├── 블록 암호 (Block Cipher)                                 │
│       │   │   ├── AES (128/192/256 bit)                               │
│       │   │   ├── DES → 3DES (deprecated)                             │
│       │   │   └── ChaCha20 (stream cipher로도 사용)                    │
│       │   │                                                            │
│       │   └── 스트림 암호 (Stream Cipher)                              │
│       │       ├── RC4 (deprecated)                                    │
│       │       └── ChaCha20-Poly1305                                   │
│       │                                                                │
│       └── 비대칭키 (Asymmetric / Public Key)                            │
│           ├── 정수 인수분해 기반                                        │
│           │   └── RSA (1024 deprecated → 2048+ 권장)                   │
│           │                                                            │
│           ├── 이산대수 문제 기반                                        │
│           │   ├── DSA (Digital Signature Algorithm)                    │
│           │   └── DH (Diffie-Hellman), ECDH                           │
│           │                                                            │
│           └── 타원곡선 (Elliptic Curve)                                 │
│               ├── ECDSA (P-256, P-384)                                │
│               ├── EdDSA (Ed25519, Ed448)                              │
│               └── ECDH (X25519)                                       │
│                                                                        │
│   [암호화 vs 서명 vs 해시]                                               │
│                                                                        │
│   암호화: 평문 ──(공개키)──► 암호문 ──(개인키)──► 평문                   │
│   서명:  평문 ──(개인키)──► 서명    ──(공개키)──► 검증                   │
│   해시:  평문 ──(단방향)──► 다이제스트 (복호 불가)                        │
│                                                                        │
└─────────────────────────────────────────────────────────────────────────┘

대칭키 vs 비대칭키 비교 (필수: 표):

비교 항목대칭키 암호화비대칭키 암호화
키 개수1개 (송수신 동일)2개 (공개키 + 개인키)
속도★ 빠름 (100~1000배)느림 (연산 복잡)
키 분배어려움 (안전한 전달 필요)쉬움 (공개키 공개 가능)
확장성O(n²) 키 필요O(n) 키 쌍
대표 알고리즘AES-256, ChaCha20RSA-2048, ECDSA-P256
주요 용도대용량 데이터 암호화키 교환, 서명, 인증
보안 강도키 길이에 비례수학적 난제에 의존

블록 암호화 모드 (필수: ASCII 아트):

┌─────────────────────────────────────────────────────────────────────────┐
│                      블록 암호화 모드 (Block Cipher Modes)               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  1. ECB (Electronic Codebook) - ❌ 사용 금지                            │
│     ┌───────────────────────────────────────────────────────────┐      │
│     │ P1 ──► AES ──► C1    P2 ──► AES ──► C2    ...            │      │
│     │    (같은 키)             (같은 키)                        │      │
│     │                                                            │      │
│     │ 문제: 같은 평문 → 같은 암호문 (패턴 노출)                  │      │
│     │ 예: 펭귄 이미지 암호화 → 펭귄 실루엣 보임                  │      │
│     └───────────────────────────────────────────────────────────┘      │
│                                                                         │
│  2. CBC (Cipher Block Chaining) - TLS 1.2까지 사용                      │
│     ┌───────────────────────────────────────────────────────────┐      │
│     │ IV ─┐                C1 ─┐                                │      │
│     │     ▼                    ▼                                │      │
│     │ P1 ──XOR──► AES ──► C1   P2 ──XOR──► AES ──► C2          │      │
│     │                                                            │      │
│     │ 특징: 이전 암호문과 XOR → 패턴 숨김                        │      │
│     │ 단점: 병렬 암호화 불가, 패딩 오라클 공격 가능              │      │
│     └───────────────────────────────────────────────────────────┘      │
│                                                                         │
│  3. CTR (Counter Mode) - 병렬 처리 가능                                 │
│     ┌───────────────────────────────────────────────────────────┐      │
│     │ Nonce||1 ──► AES ──► Key1 ──XOR P1 ──► C1                │      │
│     │ Nonce||2 ──► AES ──► Key2 ──XOR P2 ──► C2                │      │
│     │ ...                                                        │      │
│     │                                                            │      │
│     │ 장점: 병렬 처리, 랜덤 접근, 스트림처럼 사용 가능            │      │
│     │ 단점: IV 재사용 시 완전 붕괴                               │      │
│     └───────────────────────────────────────────────────────────┘      │
│                                                                         │
│  4. GCM (Galois/Counter Mode) - ★ TLS 1.3 기본, 권장                    │
│     ┌───────────────────────────────────────────────────────────┐      │
│     │                    ┌──────────────────────┐               │      │
│     │ 암호화 경로:        │ 인증 태그 생성       │               │      │
│     │ CTR 모드로 암호화   │ GHASH(C, AAD) → Tag │               │      │
│     │                    └──────────────────────┘               │      │
│     │                                                            │      │
│     │ AEAD (Authenticated Encryption with Associated Data)       │      │
│     │ → 기밀성 + 무결성을 동시에 보장                            │      │
│     │                                                            │      │
│     │ 출력: Ciphertext || Tag (16바이트)                        │      │
│     └───────────────────────────────────────────────────────────┘      │
│                                                                         │
│  [모드 선택 가이드]                                                      │
│  • 파일 암호화: AES-GCM (AEAD)                                          │
│  • TLS 통신: AES-GCM 또는 ChaCha20-Poly1305                             │
│  • 디스크 암호화: AES-XTS (XTS 모드)                                     │
│  • 레거시 호환: AES-CBC (HMAC 추가 필수)                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

RSA vs ECC 보안 강도 (필수: 표):

대칭키RSAECC (NIST)ECC (Curve25519)
80bit1024bit160bit-
112bit2048bit224bit-
128bit3072bit256bit256bit
192bit7680bit384bit-
256bit15360bit521bit448bit

핵심 알고리즘/공식 (해당 시 필수):

[AES (Advanced Encryption Standard)]

블록 크기: 128비트 (고정)
키 길이: 128/192/256비트 (라운드 수: 10/12/14)

AES-128 구조:
┌──────────────────────────────────────────────────┐
│  입력 (128비트)                                  │
│       ↓                                          │
│  [AddRoundKey - 초기 라운드]                     │
│       ↓                                          │
│  ┌──────────────────────────────────────────┐   │
│  │ Round 1~10 (또는 12, 14):                │   │
│  │   SubBytes (S-Box 치환)                  │   │
│  │   ShiftRows (행 이동)                    │   │
│  │   MixColumns (열 혼합) ← 마지막 라운드 제외│   │
│  │   AddRoundKey (라운드 키 XOR)            │   │
│  └──────────────────────────────────────────┘   │
│       ↓                                          │
│  출력 (128비트 암호문)                           │
└──────────────────────────────────────────────────┘

[RSA 암호화]

1. 키 생성:
   - 두 소수 p, q 선택 (n = p × q)
   - φ(n) = (p-1)(q-1) 계산
   - e 선택: 1 < e < φ(n), gcd(e, φ(n)) = 1
   - d 계산: d × e ≡ 1 (mod φ(n))

   공개키: (n, e)
   개인키: (n, d)

2. 암호화: C = M^e mod n
3. 복호화: M = C^d mod n
4. 서명: S = H(M)^d mod n
5. 검증: H(M) ≡ S^e mod n

[ECDH (타원곡선 Diffie-Hellman)]

곡선: y² = x³ + ax + b (mod p)
예: P-256 (secp256r1)

키 교환:
  Alice: 개인키 a, 공개키 A = aG
  Bob:   개인키 b, 공개키 B = bG

  공유 비밀:
    Alice: S = aB = a(bG) = abG
    Bob:   S = bA = b(aG) = abG

  → S의 x좌표에서 대칭키 유도

[ChaCha20-Poly1305]

ChaCha20: 스트림 암호 (Salsa20 후속)
  - 256비트 키, 96비트 논스, 32비트 카운터
  - ARX (Add-Rotate-XOR) 연산만 사용
  - 하드웨어 가속 없어도 AES보다 빠름 (모바일)

Poly1305: MAC (Message Authentication Code)
  - 일회용 키로 다이제스트 생성
  - AEAD 구성: ChaCha20 + Poly1305

[키 유도 함수 (KDF)]

PBKDF2: DK = PRF(Password, Salt, c, dkLen)
  - c: 반복 횟수 (10만~100만 권장)
  - 느려서 무차별 대입 방어

HKDF: PRK = HMAC-Hash(salt, IKM)
      OKM = HMAC-Hash(PRK, info || counter)
  - TLS 1.3에서 사용

Argon2 (2015 PWHC 우승):
  - 메모리 하드 함수 (GPU/ASIC 방어)
  - Argon2id 권장

코드 예시 (필수: Python 암호화 구현):

from dataclasses import dataclass, field
from typing import List, Dict, Optional, Tuple, Callable
from enum import Enum, auto
import hashlib
import hmac
import secrets
import os

# ============================================================
# 대칭키 암호화 시뮬레이터
# ============================================================

class BlockCipherMode(Enum):
    """블록 암호화 모드"""
    ECB = auto()
    CBC = auto()
    CTR = auto()
    GCM = auto()


@dataclass
class EncryptedData:
    """암호화 결과"""
    ciphertext: bytes
    iv: bytes = b''
    tag: bytes = b''  # GCM용
    mode: BlockCipherMode = BlockCipherMode.CBC


class SimpleBlockCipher:
    """간단한 블록 암호 시뮬레이션 (교육용)"""

    BLOCK_SIZE = 16  # 128비트

    def __init__(self, key: bytes):
        if len(key) not in (16, 24, 32):
            raise ValueError("키는 16, 24, 32바이트여야 함")
        self.key = key
        self._key_schedule = self._expand_key(key)

    def _expand_key(self, key: bytes) -> List[int]:
        """키 스케줄 생성 (시뮬레이션)"""
        # 실제 AES는 복잡한 키 확장 알고리즘 사용
        schedule = list(key)
        for i in range(10):
            schedule.append((schedule[-1] + i) % 256)
        return schedule

    def _encrypt_block(self, block: bytes) -> bytes:
        """단일 블록 암호화 (시뮬레이션)"""
        # 실제 AES: SubBytes, ShiftRows, MixColumns, AddRoundKey
        result = []
        for i, b in enumerate(block):
            key_byte = self._key_schedule[i % len(self._key_schedule)]
            result.append(b ^ key_byte)
        return bytes(result)

    def _decrypt_block(self, block: bytes) -> bytes:
        """단일 블록 복호화"""
        return self._encrypt_block(block)  # XOR은 역연산 동일


class SymmetricEncryption:
    """대칭키 암호화"""

    @staticmethod
    def pad(data: bytes, block_size: int = 16) -> bytes:
        """PKCS#7 패딩"""
        pad_len = block_size - (len(data) % block_size)
        return data + bytes([pad_len] * pad_len)

    @staticmethod
    def unpad(data: bytes) -> bytes:
        """PKCS#7 패딩 제거"""
        pad_len = data[-1]
        if pad_len > 16 or pad_len == 0:
            raise ValueError("잘못된 패딩")
        for i in range(1, pad_len + 1):
            if data[-i] != pad_len:
                raise ValueError("잘못된 패딩")
        return data[:-pad_len]

    @staticmethod
    def xor_bytes(a: bytes, b: bytes) -> bytes:
        """바이트 XOR"""
        return bytes(x ^ y for x, y in zip(a, b))

    @classmethod
    def encrypt_ecb(cls, cipher: SimpleBlockCipher,
                    plaintext: bytes) -> EncryptedData:
        """ECB 모드 암호화"""
        padded = cls.pad(plaintext)
        ciphertext = b''
        for i in range(0, len(padded), 16):
            block = padded[i:i+16]
            ciphertext += cipher._encrypt_block(block)
        return EncryptedData(ciphertext=ciphertext, mode=BlockCipherMode.ECB)

    @classmethod
    def decrypt_ecb(cls, cipher: SimpleBlockCipher,
                    data: EncryptedData) -> bytes:
        """ECB 모드 복호화"""
        plaintext = b''
        for i in range(0, len(data.ciphertext), 16):
            block = data.ciphertext[i:i+16]
            plaintext += cipher._decrypt_block(block)
        return cls.unpad(plaintext)

    @classmethod
    def encrypt_cbc(cls, cipher: SimpleBlockCipher,
                    plaintext: bytes, iv: bytes = None) -> EncryptedData:
        """CBC 모드 암호화"""
        iv = iv or secrets.token_bytes(16)
        padded = cls.pad(plaintext)
        ciphertext = b''
        prev = iv

        for i in range(0, len(padded), 16):
            block = padded[i:i+16]
            xored = cls.xor_bytes(block, prev)
            encrypted = cipher._encrypt_block(xored)
            ciphertext += encrypted
            prev = encrypted

        return EncryptedData(ciphertext=ciphertext, iv=iv, mode=BlockCipherMode.CBC)

    @classmethod
    def decrypt_cbc(cls, cipher: SimpleBlockCipher,
                    data: EncryptedData) -> bytes:
        """CBC 모드 복호화"""
        plaintext = b''
        prev = data.iv

        for i in range(0, len(data.ciphertext), 16):
            block = data.ciphertext[i:i+16]
            decrypted = cipher._decrypt_block(block)
            plaintext += cls.xor_bytes(decrypted, prev)
            prev = block

        return cls.unpad(plaintext)

    @classmethod
    def encrypt_ctr(cls, cipher: SimpleBlockCipher,
                    plaintext: bytes, nonce: bytes = None) -> EncryptedData:
        """CTR 모드 암호화"""
        nonce = nonce or secrets.token_bytes(12)
        counter = 0
        ciphertext = b''

        for i in range(0, len(plaintext), 16):
            # 카운터 블록 생성
            counter_block = nonce + counter.to_bytes(4, 'big')
            keystream = cipher._encrypt_block(counter_block)

            block = plaintext[i:i+16]
            ciphertext += cls.xor_bytes(block, keystream[:len(block)])
            counter += 1

        return EncryptedData(ciphertext=ciphertext, iv=nonce, mode=BlockCipherMode.CTR)

    @classmethod
    def decrypt_ctr(cls, cipher: SimpleBlockCipher,
                    data: EncryptedData) -> bytes:
        """CTR 모드 복호화 (암호화와 동일)"""
        return cls.encrypt_ctr(cipher, data.ciphertext, data.iv).ciphertext


# ============================================================
# 비대칭키 암호화 시뮬레이터 (RSA 단순화)
# ============================================================

@dataclass
class RSAKeyPair:
    """RSA 키 쌍"""
    n: int  # 모듈러스
    e: int  # 공개 지수
    d: int  # 개인 지수
    p: int = 0  # 소수1 (개인키)
    q: int = 0  # 소수2 (개인키)

    @property
    def public_key(self) -> Tuple[int, int]:
        return (self.n, self.e)

    @property
    def private_key(self) -> Tuple[int, int]:
        return (self.n, self.d)

    @property
    def key_size(self) -> int:
        return self.n.bit_length()


class SimpleRSA:
    """RSA 시뮬레이션 (교육용 - 작은 소수만 사용)"""

    @staticmethod
    def gcd(a: int, b: int) -> int:
        """최대공약수"""
        while b:
            a, b = b, a % b
        return a

    @staticmethod
    def extended_gcd(a: int, b: int) -> Tuple[int, int, int]:
        """확장 유클리드 알고리즘"""
        if a == 0:
            return b, 0, 1
        gcd_val, x1, y1 = SimpleRSA.extended_gcd(b % a, a)
        x = y1 - (b // a) * x1
        y = x1
        return gcd_val, x, y

    @staticmethod
    def mod_inverse(e: int, phi: int) -> int:
        """모듈러 역원 계산"""
        gcd_val, x, _ = SimpleRSA.extended_gcd(e % phi, phi)
        if gcd_val != 1:
            raise ValueError("역원이 존재하지 않음")
        return (x % phi + phi) % phi

    @staticmethod
    def is_prime(n: int, k: int = 5) -> bool:
        """밀러-라빈 소수 판정"""
        if n < 2:
            return False
        if n == 2 or n == 3:
            return True
        if n % 2 == 0:
            return False

        r, d = 0, n - 1
        while d % 2 == 0:
            r += 1
            d //= 2

        for _ in range(k):
            a = secrets.randbelow(n - 3) + 2
            x = pow(a, d, n)
            if x == 1 or x == n - 1:
                continue
            for _ in range(r - 1):
                x = pow(x, 2, n)
                if x == n - 1:
                    break
            else:
                return False
        return True

    @staticmethod
    def generate_prime(bits: int) -> int:
        """소수 생성"""
        while True:
            p = secrets.randbits(bits)
            p |= (1 << bits - 1) | 1  # 최상위/최하위 비트 1
            if SimpleRSA.is_prime(p):
                return p

    @classmethod
    def generate_keypair(cls, bits: int = 2048) -> RSAKeyPair:
        """RSA 키 쌍 생성"""
        # 실제로는 더 큰 소수 사용 (여기서는 시뮬레이션)
        half_bits = bits // 2

        p = cls.generate_prime(half_bits)
        q = cls.generate_prime(half_bits)
        while p == q:
            q = cls.generate_prime(half_bits)

        n = p * q
        phi = (p - 1) * (q - 1)

        # 공개 지수 선택 (일반적으로 65537)
        e = 65537
        while cls.gcd(e, phi) != 1:
            e += 2

        # 개인 지수 계산
        d = cls.mod_inverse(e, phi)

        return RSAKeyPair(n=n, e=e, d=d, p=p, q=q)

    @staticmethod
    def encrypt(plaintext: bytes, public_key: Tuple[int, int]) -> int:
        """RSA 암호화"""
        n, e = public_key
        m = int.from_bytes(plaintext, 'big')
        if m >= n:
            raise ValueError("평문이 너무 김")
        return pow(m, e, n)

    @staticmethod
    def decrypt(ciphertext: int, private_key: Tuple[int, int]) -> bytes:
        """RSA 복호화"""
        n, d = private_key
        m = pow(ciphertext, d, n)
        # 바이트로 변환 (길이 계산)
        byte_len = (n.bit_length() + 7) // 8
        return m.to_bytes(byte_len, 'big').lstrip(b'\x00')

    @staticmethod
    def sign(message: bytes, private_key: Tuple[int, int]) -> int:
        """RSA 서명"""
        n, d = private_key
        h = int.from_bytes(hashlib.sha256(message).digest(), 'big')
        return pow(h, d, n)

    @staticmethod
    def verify(message: bytes, signature: int,
               public_key: Tuple[int, int]) -> bool:
        """RSA 서명 검증"""
        n, e = public_key
        h = int.from_bytes(hashlib.sha256(message).digest(), 'big')
        verified = pow(signature, e, n)
        return h == verified


# ============================================================
# 해시 함수 및 MAC
# ============================================================

class HashFunctions:
    """해시 함수"""

    @staticmethod
    def sha256(data: bytes) -> bytes:
        """SHA-256"""
        return hashlib.sha256(data).digest()

    @staticmethod
    def sha512(data: bytes) -> bytes:
        """SHA-512"""
        return hashlib.sha512(data).digest()

    @staticmethod
    def hmac_sha256(key: bytes, data: bytes) -> bytes:
        """HMAC-SHA256"""
        return hmac.new(key, data, hashlib.sha256).digest()


class PasswordHashing:
    """비밀번호 해싱"""

    @staticmethod
    def pbkdf2(password: str, salt: bytes = None,
               iterations: int = 100000,
               key_length: int = 32) -> Tuple[bytes, bytes]:
        """PBKDF2 키 유도"""
        salt = salt or secrets.token_bytes(16)
        key = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, iterations, key_length)
        return key, salt

    @staticmethod
    def verify_password(password: str, stored_key: bytes,
                        salt: bytes, iterations: int) -> bool:
        """비밀번호 검증"""
        key, _ = PasswordHashing.pbkdf2(password, salt, iterations, len(stored_key))
        return hmac.compare_digest(key, stored_key)


# ============================================================
# 하이브리드 암호화 (TLS 방식 시뮬레이션)
# ============================================================

class HybridEncryption:
    """하이브리드 암호화 (RSA + AES)"""

    @staticmethod
    def encrypt(plaintext: bytes,
                recipient_public_key: Tuple[int, int]) -> Dict:
        """
        하이브리드 암호화:
        1. 무작위 AES 키 생성
        2. AES 키로 평문 암호화
        3. RSA로 AES 키 암호화
        """
        # 1. AES-256 키 생성
        aes_key = secrets.token_bytes(32)
        iv = secrets.token_bytes(16)

        # 2. AES로 평문 암호화 (CBC 모드)
        cipher = SimpleBlockCipher(aes_key)
        encrypted = SymmetricEncryption.encrypt_cbc(cipher, plaintext, iv)

        # 3. RSA로 AES 키 암호화
        encrypted_key = SimpleRSA.encrypt(aes_key, recipient_public_key)

        return {
            'encrypted_key': encrypted_key,
            'iv': iv,
            'ciphertext': encrypted.ciphertext
        }

    @staticmethod
    def decrypt(encrypted_data: Dict,
                recipient_private_key: Tuple[int, int]) -> bytes:
        """하이브리드 복호화"""
        # 1. RSA로 AES 키 복호화
        aes_key = SimpleRSA.decrypt(encrypted_data['encrypted_key'],
                                     recipient_private_key)

        # 2. AES로 암호문 복호화
        cipher = SimpleBlockCipher(aes_key)
        data = EncryptedData(
            ciphertext=encrypted_data['ciphertext'],
            iv=encrypted_data['iv'],
            mode=BlockCipherMode.CBC
        )
        return SymmetricEncryption.decrypt_cbc(cipher, data)


# ============================================================
# 사용 예시
# ============================================================

if __name__ == "__main__":
    print("=" * 60)
    print("         암호화 기술 데모")
    print("=" * 60)

    # 1. 대칭키 암호화
    print("\n1. 대칭키 암호화 (AES)")
    print("-" * 40)

    key = secrets.token_bytes(32)  # AES-256
    cipher = SimpleBlockCipher(key)
    plaintext = b"Hello, World! This is a secret message."

    # CBC 모드
    encrypted_cbc = SymmetricEncryption.encrypt_cbc(cipher, plaintext)
    decrypted_cbc = SymmetricEncryption.decrypt_cbc(cipher, encrypted_cbc)
    print(f"원문: {plaintext}")
    print(f"CBC 암호화: {encrypted_cbc.ciphertext.hex()[:50]}...")
    print(f"CBC 복호화: {decrypted_cbc}")
    print(f"일치: {plaintext == decrypted_cbc}")

    # CTR 모드
    encrypted_ctr = SymmetricEncryption.encrypt_ctr(cipher, plaintext)
    decrypted_ctr = SymmetricEncryption.decrypt_ctr(cipher, encrypted_ctr)
    print(f"\nCTR 복호화: {decrypted_ctr}")
    print(f"일치: {plaintext == decrypted_ctr}")

    # 2. 비대칭키 암호화 (RSA)
    print("\n\n2. 비대칭키 암호화 (RSA)")
    print("-" * 40)

    # 키 생성 (시뮬레이션 - 작은 키)
    keypair = SimpleRSA.generate_keypair(bits=512)  # 실제는 2048+
    print(f"키 크기: {keypair.key_size}비트")
    print(f"공개 지수 e: {keypair.e}")

    # 암호화/복호화
    message = b"Secret"
    encrypted = SimpleRSA.encrypt(message, keypair.public_key)
    decrypted = SimpleRSA.decrypt(encrypted, keypair.private_key)
    print(f"\n원문: {message}")
    print(f"암호화: {encrypted}")
    print(f"복호화: {decrypted.strip(b'\\x00')}")

    # 서명/검증
    signature = SimpleRSA.sign(message, keypair.private_key)
    verified = SimpleRSA.verify(message, signature, keypair.public_key)
    print(f"\n서명: {signature}")
    print(f"검증: {'성공' if verified else '실패'}")

    # 3. 해시 및 MAC
    print("\n\n3. 해시 함수 및 MAC")
    print("-" * 40)

    data = b"Hello, World!"
    print(f"SHA-256: {HashFunctions.sha256(data).hex()}")
    print(f"SHA-512: {HashFunctions.sha512(data).hex()[:64]}...")

    mac_key = b"secret_key"
    mac = HashFunctions.hmac_sha256(mac_key, data)
    print(f"HMAC-SHA256: {mac.hex()}")

    # 4. 비밀번호 해싱
    print("\n\n4. 비밀번호 해싱 (PBKDF2)")
    print("-" * 40)

    password = "MySecurePassword123!"
    key, salt = PasswordHashing.pbkdf2(password, iterations=100000)
    print(f"솔트: {salt.hex()}")
    print(f"유도된 키: {key.hex()}")

    # 검증
    valid = PasswordHashing.verify_password(password, key, salt, 100000)
    print(f"비밀번호 검증: {'성공' if valid else '실패'}")

    # 잘못된 비밀번호
    invalid = PasswordHashing.verify_password("wrong", key, salt, 100000)
    print(f"잘못된 비밀번호: {'성공' if invalid else '실패'}")

    # 5. 하이브리드 암호화
    print("\n\n5. 하이브리드 암호화 (RSA + AES)")
    print("-" * 40)

    message = b"This is a large message that needs to be encrypted securely."
    encrypted = HybridEncryption.encrypt(message, keypair.public_key)
    decrypted = HybridEncryption.decrypt(encrypted, keypair.private_key)

    print(f"원문: {message}")
    print(f"암호화된 AES 키: {encrypted['encrypted_key']}")
    print(f"암호문 길이: {len(encrypted['ciphertext'])}바이트")
    print(f"복호화: {decrypted}")
    print(f"일치: {message == decrypted}")