암호화
Encryption
데이터를 허가된 사용자만 읽을 수 있는 형태로 변환하는 핵심 보안 기술입니다. 평문(Plaintext)을 암호문(Ciphertext)으로 변환하여 기밀성을 보장하며, 대칭키(AES), 비대칭키(RSA), 해시(SHA-256) 등 다양한 알고리즘이 용도에 따라 사용됩니다.
Encryption
데이터를 허가된 사용자만 읽을 수 있는 형태로 변환하는 핵심 보안 기술입니다. 평문(Plaintext)을 암호문(Ciphertext)으로 변환하여 기밀성을 보장하며, 대칭키(AES), 비대칭키(RSA), 해시(SHA-256) 등 다양한 알고리즘이 용도에 따라 사용됩니다.
스키탈레(Scytale) 암호 - 막대에 가죽을 감아 메시지를 숨김. 시저 암호(Caesar Cipher) - 알파벳을 일정 수만큼 밀어서 치환.
독일의 에니그마(Enigma) 기계. 앨런 튜링이 해독에 성공하여 2차 세계대전 종전을 앞당김. 현대 컴퓨터 과학의 시초.
Diffie-Hellman 키 교환 발표. 1977년 RSA 알고리즘 개발. 키 배포 문제 해결로 인터넷 보안의 기반 마련.
미국 NIST에서 AES(Advanced Encryption Standard)를 표준으로 채택. 전 세계 정부, 금융기관, 기업의 표준 암호화 방식으로 사용.
양자 컴퓨터 위협에 대비한 Post-Quantum Cryptography(PQC) 연구. NIST에서 CRYSTALS-Kyber, CRYSTALS-Dilithium 등 표준화 진행.
암호화는 정보보안의 핵심 3요소를 실현하는 기술적 수단입니다.
| 알고리즘 | 유형 | 키 크기 | 속도 | 주요 용도 | 보안 상태 |
|---|---|---|---|---|---|
AES-256 |
대칭키 | 256 bit | 매우 빠름 | 파일 암호화, TLS, 디스크 | 권장 |
ChaCha20 |
대칭키 | 256 bit | 매우 빠름 | 모바일, IoT, TLS 1.3 | 권장 |
RSA-2048 |
비대칭키 | 2048 bit | 느림 | 키 교환, 디지털 서명 | 권장 (4096 선호) |
ECC P-256 |
비대칭키 | 256 bit | 빠름 | TLS, 인증서, 서명 | 권장 |
SHA-256 |
해시 | 256 bit | 매우 빠름 | 무결성 검증, 블록체인 | 권장 |
Argon2id |
해시(KDF) | 가변 | 의도적 지연 | 패스워드 저장 | 최고 권장 |
DES |
대칭키 | 56 bit | 빠름 | 레거시 시스템 | 사용 금지 |
MD5 |
해시 | 128 bit | 빠름 | 체크섬(비보안) | 보안 용도 금지 |
# AES-256-GCM 암호화 (Python cryptography 라이브러리) from cryptography.hazmat.primitives.ciphers.aead import AESGCM import os import base64 class AESEncryptor: """AES-256-GCM 암호화 클래스 (인증된 암호화)""" def __init__(self, key: bytes = None): # 키가 없으면 256비트(32바이트) 랜덤 키 생성 self.key = key or AESGCM.generate_key(bit_length=256) self.aesgcm = AESGCM(self.key) def encrypt(self, plaintext: str, associated_data: bytes = None) -> str: """평문을 암호화하여 Base64 문자열로 반환""" # 12바이트 nonce (GCM 권장 크기) nonce = os.urandom(12) # 암호화 (인증 태그 자동 추가) ciphertext = self.aesgcm.encrypt( nonce, plaintext.encode('utf-8'), associated_data ) # nonce + ciphertext를 Base64로 인코딩 return base64.b64encode(nonce + ciphertext).decode('utf-8') def decrypt(self, encrypted: str, associated_data: bytes = None) -> str: """암호문을 복호화하여 평문 반환""" data = base64.b64decode(encrypted) nonce, ciphertext = data[:12], data[12:] # 복호화 (인증 실패 시 예외 발생) plaintext = self.aesgcm.decrypt(nonce, ciphertext, associated_data) return plaintext.decode('utf-8') # 사용 예시 encryptor = AESEncryptor() # 민감한 데이터 암호화 secret_data = "주민등록번호: 901234-1234567" encrypted = encryptor.encrypt(secret_data) print(f"암호화: {encrypted}") # 복호화 decrypted = encryptor.decrypt(encrypted) print(f"복호화: {decrypted}") # 키 안전 저장 (실제로는 KMS, HSM 사용) key_b64 = base64.b64encode(encryptor.key).decode() print(f"키(안전 저장 필요): {key_b64}")
# RSA 암호화 + 디지털 서명 (Python cryptography) from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization import base64 class RSACrypto: """RSA 암호화 및 디지털 서명""" def __init__(self, key_size: int = 4096): # RSA 키 쌍 생성 (최소 2048, 권장 4096) self.private_key = rsa.generate_private_key( public_exponent=65537, key_size=key_size ) self.public_key = self.private_key.public_key() def encrypt(self, plaintext: str) -> str: """공개키로 암호화 (상대방이 보낸 공개키 사용)""" ciphertext = self.public_key.encrypt( plaintext.encode('utf-8'), padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return base64.b64encode(ciphertext).decode('utf-8') def decrypt(self, ciphertext: str) -> str: """개인키로 복호화 (자신의 개인키 사용)""" plaintext = self.private_key.decrypt( base64.b64decode(ciphertext), padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return plaintext.decode('utf-8') def sign(self, message: str) -> str: """개인키로 서명 (발신자 신원 증명)""" signature = self.private_key.sign( message.encode('utf-8'), padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) return base64.b64encode(signature).decode('utf-8') def verify(self, message: str, signature: str) -> bool: """공개키로 서명 검증 (발신자 확인)""" try: self.public_key.verify( base64.b64decode(signature), message.encode('utf-8'), padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) return True except: return False # 사용 예시: 안전한 메시지 전송 alice = RSACrypto() bob = RSACrypto() # Alice가 Bob에게 암호화된 메시지 전송 message = "계약금 1억원을 아래 계좌로 송금" # Bob의 공개키로 암호화 (Bob만 복호화 가능) encrypted = bob.encrypt(message) # Alice의 개인키로 서명 (Alice가 보낸 것 증명) signature = alice.sign(message) # Bob이 수신 후 decrypted = bob.decrypt(encrypted) is_valid = alice.verify(decrypted, signature) # Alice 공개키로 검증 print(f"메시지: {decrypted}") print(f"서명 검증: {'유효' if is_valid else '위조 의심'}")
# 패스워드 안전 저장 (bcrypt + Argon2) import bcrypt from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError import secrets class PasswordManager: """안전한 패스워드 관리 (Argon2id 권장)""" def __init__(self, use_argon2: bool = True): self.use_argon2 = use_argon2 if use_argon2: # Argon2id: 메모리 하드 + GPU 저항 self.hasher = PasswordHasher( time_cost=3, # 반복 횟수 memory_cost=65536, # 64MB 메모리 parallelism=4, # 4 스레드 hash_len=32, # 해시 길이 salt_len=16 # Salt 길이 ) def hash_password(self, password: str) -> str: """패스워드를 해시하여 저장용 문자열 반환""" if self.use_argon2: return self.hasher.hash(password) else: # bcrypt: 자동 salt 생성, cost factor 12 권장 salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode(), salt).decode() def verify_password(self, password: str, hash_str: str) -> bool: """입력된 패스워드가 저장된 해시와 일치하는지 확인""" try: if self.use_argon2: self.hasher.verify(hash_str, password) return True else: return bcrypt.checkpw(password.encode(), hash_str.encode()) except (VerifyMismatchError, ValueError): return False def needs_rehash(self, hash_str: str) -> bool: """해시 파라미터 업그레이드 필요 여부 확인""" if self.use_argon2: return self.hasher.check_needs_rehash(hash_str) return False # 사용 예시: 회원가입/로그인 pm = PasswordManager(use_argon2=True) # 회원가입 시 user_password = "MySecure@Pass123!" stored_hash = pm.hash_password(user_password) print(f"DB 저장: {stored_hash}") # 로그인 시 login_attempt = "MySecure@Pass123!" if pm.verify_password(login_attempt, stored_hash): print("로그인 성공!") # 해시 파라미터 업그레이드 체크 if pm.needs_rehash(stored_hash): new_hash = pm.hash_password(login_attempt) print("해시 업그레이드 완료") else: print("로그인 실패") # 패스워드 강도 검증 추가 권장 def check_password_strength(password: str) -> dict: return { "length": len(password) >= 12, "uppercase": any(c.isupper() for c in password), "lowercase": any(c.islower() for c in password), "digit": any(c.isdigit() for c in password), "special": any(c in "!@#$%^&*" for c in password) }
"Don't roll your own crypto" - 직접 만든 암호화 알고리즘은 반드시 취약점이 있습니다. AES, RSA, SHA-256 등 검증된 표준만 사용하세요.
AES-ECB는 같은 평문이 같은 암호문이 되어 패턴이 노출됩니다. 반드시 AES-GCM 또는 AES-CBC(IV 사용)를 사용하세요.
암호화 키를 코드에 하드코딩하거나 Git에 커밋하면 암호화 의미가 없습니다. AWS KMS, HashiCorp Vault, Azure Key Vault 등 전용 키 관리 시스템을 사용하세요.
2030년대 양자 컴퓨터가 RSA, ECC를 무력화할 수 있습니다. 현재 수집된 암호화 데이터가 미래에 해독될 수 있으니, 민감 데이터는 Post-Quantum 암호화 전환을 계획하세요.
사건: Adobe에서 1억 5,300만 개의 사용자 계정 정보가 유출되었습니다. 패스워드가 AES-ECB 모드로 암호화되어 있었는데, 같은 패스워드는 같은 암호문이 되는 ECB의 특성으로 인해 패턴 분석이 가능했습니다.
문제점: 패스워드에 양방향 암호화(AES)를 사용한 것 자체가 잘못이며, ECB 모드는 패턴을 숨기지 못합니다. 가장 많이 사용된 패스워드 "123456"의 암호문이 동일하여 쉽게 역추적되었습니다.
패스워드는 반드시 해시(bcrypt, Argon2)로 저장. 암호화 시 ECB 모드 절대 금지.
사건: OpenSSL의 Heartbeat 확장 기능에서 버퍼 오버리드 취약점이 발견되었습니다. 공격자가 서버 메모리의 64KB를 읽을 수 있어, 암호화에 사용되는 개인키가 노출되었습니다.
영향: 전 세계 웹 서버의 약 17%(50만 대 이상)가 영향을 받았습니다. 개인키가 노출되면 과거에 캡처된 모든 TLS 통신이 복호화될 수 있습니다.
암호화 라이브러리 정기 업데이트 필수. Forward Secrecy(ECDHE) 사용으로 키 노출 피해 최소화.
사건: WannaCry 랜섬웨어가 150개국 30만 대 이상의 컴퓨터를 감염시켰습니다. 피해자의 파일을 AES로 암호화한 후 RSA 공개키로 AES 키를 암호화하여, 공격자의 개인키 없이는 복호화가 불가능했습니다.
피해: 영국 NHS(국민건강서비스)는 수술 취소, 구급차 우회 등 의료 서비스가 마비되었습니다. 전 세계 피해액은 약 40~80억 달러로 추산됩니다.
암호화는 양날의 검. 정기 백업, 패치 관리, 네트워크 분리로 랜섬웨어 대비.