Cryptography
암호학 (Cryptography)
Cryptography(암호학)은 정보를 안전하게 보호하기 위한 수학적 기법과 프로토콜의 학문입니다. 대칭키 암호화(AES), 비대칭키 암호화(RSA, ECC), 해시 함수(SHA-256)를 포함하며, 데이터의 기밀성, 무결성, 인증, 부인 방지를 보장합니다.
암호학 (Cryptography)
Cryptography(암호학)은 정보를 안전하게 보호하기 위한 수학적 기법과 프로토콜의 학문입니다. 대칭키 암호화(AES), 비대칭키 암호화(RSA, ECC), 해시 함수(SHA-256)를 포함하며, 데이터의 기밀성, 무결성, 인증, 부인 방지를 보장합니다.
Cryptography(암호학)은 고대 시저 암호에서 시작해 현대 컴퓨터 보안의 핵심이 된 학문입니다. "암호(crypto)"는 그리스어로 "숨겨진"을, "graphein"은 "쓰기"를 의미합니다. 현대 암호학은 단순한 문자 치환을 넘어, 복잡한 수학적 문제(소인수분해, 이산 로그, 타원 곡선)를 기반으로 합니다. 암호학의 네 가지 핵심 목표는 기밀성(Confidentiality), 무결성(Integrity), 인증(Authentication), 부인 방지(Non-repudiation)입니다.
대칭키 암호화(Symmetric Encryption)는 암호화와 복호화에 동일한 키를 사용합니다. AES(Advanced Encryption Standard)가 대표적이며, 128/192/256비트 키를 지원합니다. 대칭키는 빠르고 효율적이지만, 키 교환 문제가 있습니다. 비대칭키 암호화(Asymmetric Encryption)는 공개키와 개인키 쌍을 사용해 이 문제를 해결합니다. RSA, ECC(Elliptic Curve Cryptography)가 대표적이며, RSA는 소인수분해의 어려움을, ECC는 타원곡선 이산 로그 문제를 기반으로 합니다.
실무에서는 하이브리드 암호화를 사용합니다. TLS 핸드셰이크에서 ECDHE(비대칭키)로 세션 키를 안전하게 교환하고, 이후 통신은 AES-GCM(대칭키)으로 암호화합니다. 비대칭키의 안전한 키 교환과 대칭키의 성능 장점을 모두 취하는 방식입니다. 디지털 서명(RSA-PSS, ECDSA)은 비대칭키로 메시지의 출처와 무결성을 검증합니다.
양자 컴퓨터의 발전으로 RSA와 ECC가 위협받고 있습니다. Shor 알고리즘을 사용하는 양자 컴퓨터는 RSA 2048비트 키를 수 시간 내에 해독할 수 있습니다. NIST는 2024년 포스트 양자 암호화(PQC) 표준을 발표했습니다. ML-KEM(키 캡슐화), ML-DSA(디지털 서명)가 새 표준으로, 격자 기반 암호화를 사용합니다. AES-256 같은 대칭키 암호는 Grover 알고리즘에도 키 크기를 두 배로 늘리면 안전합니다. 2025년 현재, 조직들은 "하이브리드 접근법"으로 PQC 마이그레이션을 준비하고 있습니다.
# AES-256-GCM 암호화 예제 (Python)
# pip install cryptography
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
import base64
class AESEncryptor:
"""AES-256-GCM 암호화 클래스"""
def __init__(self, password: str):
# 비밀번호에서 256비트 키 파생 (PBKDF2)
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32, # 256비트 = 32바이트
salt=salt,
iterations=600000, # 2025 OWASP 권장
)
self.key = kdf.derive(password.encode())
self.salt = salt
def encrypt(self, plaintext: str) -> dict:
"""
AES-256-GCM 암호화
- GCM 모드: 암호화 + 인증(무결성 보장)을 동시에
- Nonce(IV): 각 암호화마다 고유해야 함 (절대 재사용 금지!)
"""
# 12바이트 nonce (GCM 권장 크기)
nonce = os.urandom(12)
aesgcm = AESGCM(self.key)
ciphertext = aesgcm.encrypt(
nonce,
plaintext.encode('utf-8'),
associated_data=None # AAD: 추가 인증 데이터 (옵션)
)
return {
'ciphertext': base64.b64encode(ciphertext).decode(),
'nonce': base64.b64encode(nonce).decode(),
'salt': base64.b64encode(self.salt).decode()
}
def decrypt(self, encrypted_data: dict) -> str:
"""AES-256-GCM 복호화"""
nonce = base64.b64decode(encrypted_data['nonce'])
ciphertext = base64.b64decode(encrypted_data['ciphertext'])
aesgcm = AESGCM(self.key)
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
return plaintext.decode('utf-8')
# 사용 예시
encryptor = AESEncryptor("my-secure-password-2025!")
# 암호화
message = "민감한 데이터: 신용카드 번호 4242-****-****-4242"
encrypted = encryptor.encrypt(message)
print(f"암호화된 데이터: {encrypted['ciphertext'][:50]}...")
# 복호화
decrypted = encryptor.decrypt(encrypted)
print(f"복호화된 데이터: {decrypted}")
# 주의사항:
# 1. Nonce는 절대 재사용 금지! (같은 키로 같은 nonce 사용 = 보안 붕괴)
# 2. CBC 모드 대신 GCM 모드 사용 (Padding Oracle 공격 방지)
# 3. ECB 모드는 절대 사용 금지 (패턴 노출)
# RSA 및 하이브리드 암호화 예제 (Python)
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64
class HybridEncryptor:
"""
하이브리드 암호화 (RSA + AES)
- RSA로 대칭키 암호화 (키 교환)
- AES로 실제 데이터 암호화 (성능)
"""
def __init__(self):
# RSA 4096비트 키 쌍 생성 (2048은 최소, 4096 권장)
self.private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096
)
self.public_key = self.private_key.public_key()
def get_public_key_pem(self) -> bytes:
"""공개키를 PEM 형식으로 내보내기"""
return self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
def encrypt(self, plaintext: str, recipient_public_key) -> dict:
"""
하이브리드 암호화:
1. 랜덤 AES 세션 키 생성
2. RSA-OAEP로 세션 키 암호화
3. AES-GCM으로 데이터 암호화
"""
# 1. 랜덤 256비트 AES 세션 키 생성
session_key = os.urandom(32)
nonce = os.urandom(12)
# 2. RSA-OAEP로 세션 키 암호화 (PSS 패딩 권장)
encrypted_key = recipient_public_key.encrypt(
session_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 3. AES-GCM으로 데이터 암호화
aesgcm = AESGCM(session_key)
ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)
return {
'encrypted_key': base64.b64encode(encrypted_key).decode(),
'ciphertext': base64.b64encode(ciphertext).decode(),
'nonce': base64.b64encode(nonce).decode()
}
def decrypt(self, encrypted_data: dict) -> str:
"""하이브리드 복호화"""
# 1. RSA로 세션 키 복호화
encrypted_key = base64.b64decode(encrypted_data['encrypted_key'])
session_key = self.private_key.decrypt(
encrypted_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 2. AES로 데이터 복호화
nonce = base64.b64decode(encrypted_data['nonce'])
ciphertext = base64.b64decode(encrypted_data['ciphertext'])
aesgcm = AESGCM(session_key)
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
return plaintext.decode()
# 디지털 서명 예제
from cryptography.hazmat.primitives.asymmetric import padding as sign_padding
def sign_message(private_key, message: bytes) -> bytes:
"""RSA-PSS 디지털 서명"""
signature = private_key.sign(
message,
sign_padding.PSS(
mgf=sign_padding.MGF1(hashes.SHA256()),
salt_length=sign_padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return signature
def verify_signature(public_key, message: bytes, signature: bytes) -> bool:
"""서명 검증"""
try:
public_key.verify(
signature, message,
sign_padding.PSS(
mgf=sign_padding.MGF1(hashes.SHA256()),
salt_length=sign_padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True
except Exception:
return False
// Node.js 암호화 예제
const crypto = require('crypto');
// === AES-256-GCM 암호화 ===
class AESCrypto {
constructor(password) {
// 비밀번호에서 키 파생 (scrypt 사용)
this.salt = crypto.randomBytes(16);
this.key = crypto.scryptSync(password, this.salt, 32); // 256비트
}
encrypt(plaintext) {
// 12바이트 IV (GCM 권장)
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
encrypted += cipher.final('base64');
// Auth Tag (GCM 무결성 검증용)
const authTag = cipher.getAuthTag();
return {
ciphertext: encrypted,
iv: iv.toString('base64'),
authTag: authTag.toString('base64'),
salt: this.salt.toString('base64')
};
}
decrypt(encryptedData) {
const decipher = crypto.createDecipheriv(
'aes-256-gcm',
this.key,
Buffer.from(encryptedData.iv, 'base64')
);
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'base64'));
let decrypted = decipher.update(encryptedData.ciphertext, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// === RSA 키 쌍 생성 및 암호화 ===
function generateRSAKeyPair() {
return crypto.generateKeyPairSync('rsa', {
modulusLength: 4096, // 4096비트 권장
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
}
function rsaEncrypt(publicKey, data) {
return crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256'
},
Buffer.from(data)
).toString('base64');
}
// === 해시 및 HMAC ===
function hashSHA256(data) {
return crypto.createHash('sha256').update(data).digest('hex');
}
function hmacSHA256(key, data) {
return crypto.createHmac('sha256', key).update(data).digest('hex');
}
// === ECDH 키 교환 (Perfect Forward Secrecy) ===
function ecdhKeyExchange() {
// Alice와 Bob의 ECDH 키 쌍 생성
const alice = crypto.createECDH('prime256v1'); // P-256
alice.generateKeys();
const bob = crypto.createECDH('prime256v1');
bob.generateKeys();
// 공개키 교환 후 공유 비밀 계산
const aliceSecret = alice.computeSecret(bob.getPublicKey());
const bobSecret = bob.computeSecret(alice.getPublicKey());
// 두 비밀이 동일함! (이것으로 세션 키 파생)
console.log('Shared secret match:', aliceSecret.equals(bobSecret));
return aliceSecret;
}
// 사용 예시
const aes = new AESCrypto('my-secure-password');
const encrypted = aes.encrypt('비밀 메시지');
console.log('Encrypted:', encrypted.ciphertext);
console.log('Decrypted:', aes.decrypt(encrypted));
"민감한 데이터는 AES-256-GCM으로 암호화하고 있습니다. 키 관리는 AWS KMS를 사용하고, 키 로테이션은 연 1회 자동으로 수행됩니다. 데이터베이스 암호화 키와 애플리케이션 레벨 암호화 키를 분리해서 계층적으로 관리합니다."
"ECB 모드 대신 GCM 모드를 사용해야 합니다. ECB는 같은 평문 블록이 같은 암호문 블록을 생성해서 패턴이 노출됩니다. 그리고 IV(Nonce)는 매번 새로 생성해야 합니다. 같은 키로 같은 IV를 재사용하면 보안이 완전히 무너집니다."
"장기 보관 데이터는 양자 컴퓨터 위협을 고려해야 합니다. 'Harvest Now, Decrypt Later' 공격에 대비해 민감한 데이터는 하이브리드 암호화(기존 알고리즘 + PQC)로 전환을 검토 중입니다. NIST ML-KEM-768이 범용 권장 사항입니다."
"Don't roll your own crypto" - 암호화 알고리즘을 직접 구현하지 마세요. 검증된 라이브러리(OpenSSL, libsodium, cryptography)를 사용하세요. 미세한 구현 오류가 치명적 취약점이 됩니다.
MD5, SHA-1, DES, RC4, ECB 모드는 더 이상 안전하지 않습니다. SHA-256 이상, AES-256, RSA 2048+ 또는 ECC P-256 이상을 사용하세요. PKCS#1 v1.5 패딩 대신 OAEP 또는 PSS를 사용하세요.
암호화 키를 소스 코드에 하드코딩하면 안 됩니다. 환경 변수, 시크릿 매니저(AWS Secrets Manager, HashiCorp Vault), 또는 KMS를 사용하세요. Git에 키가 푸시되면 즉시 로테이션하세요.
AES-256-GCM 사용, 고유한 IV/Nonce 생성, 키 로테이션 자동화, HSM/KMS로 키 보호, Crypto-agility(알고리즘 교체 가능한 설계), 장기 데이터는 PQC 하이브리드 검토. 암호화와 함께 해시(무결성), 서명(인증)을 조합하세요.