"적이 모든 대화를 엿듣고 있는데, 어떻게 너와 나만의 비밀을 만들 수 있을까?" 1976년, 두 명의 천재가 이 모순적인 질문에 답을 내놓았다.
🎯 이 글에서 다루는 것
Diffie-Hellman(DH)이 등장한 역사적 배경
페인트 비유로 이해하는 직관적 동작 원리
모듈러 지수와 이산 대수 문제라는 수학적 기반
Python 코드로 직접 구현해 보는 키 교환
중간자 공격(MITM)과 ECDH로의 진화
TLS·SSH 같은 실무 환경에서의 활용
📌 도입 — 만나기도 전에 비밀 키를 어떻게 공유할까
암호화의 가장 큰 골칫거리는 의외로 "암호 알고리즘"이 아닙니다. 진짜 어려운 것은 암호화에 사용할 키를 어떻게 안전하게 공유할 것인가입니다.
1976년 이전, 두 사람이 비밀 통신을 하려면 사전에 직접 만나서 같은 키를 공유하거나, 신뢰할 수 있는 운반자(courier)를 통해 키가 적힌 종이를 전달해야 했습니다. 냉전 시대 첩보영화를 떠올려 보시면 됩니다.
그런데 인터넷 시대에는 어떻게 할까요? 한 번도 만난 적 없는 서버와 클라이언트가, 그것도 모든 통신이 도청 가능한 공개 네트워크 위에서, 어떻게 둘만의 비밀 키를 만들어 낼 수 있을까요?
이 모순처럼 보이는 문제를 풀어낸 것이 바로 Whitfield Diffie와 Martin Hellman이 제안한 Diffie-Hellman 키 교환 알고리즘입니다. 현대 인터넷 보안의 기초를 만든 발명이자, 2015년 두 사람에게 튜링상을 안긴 업적이지요.
🔍 페인트 비유 — 5분 만에 이해하는 DH
수학으로 들어가기 전에, DH를 가장 직관적으로 설명하는 페인트 비유부터 보겠습니다.
앨리스와 밥은 공개적으로 노란색 페인트를 약속합니다. 적도 이걸 봅니다.
앨리스는 자기만의 비밀 색(빨강)을 노란색에 섞어 주황색을 만들고, 밥에게 보냅니다.
밥도 자기만의 비밀 색(파랑)을 노란색에 섞어 초록색을 만들고, 앨리스에게 보냅니다.
앨리스는 받은 초록색에 자기 비밀 색(빨강)을 섞습니다 → 노랑+파랑+빨강
밥은 받은 주황색에 자기 비밀 색(파랑)을 섞습니다 → 노랑+빨강+파랑
결과적으로 두 사람은 같은 색을 갖게 됩니다. 그런데 도청자는 노란색, 주황색, 초록색만 봤을 뿐입니다. 이미 섞여 버린 페인트에서 원래의 비밀 색을 분리해 내는 일은 사실상 불가능하기에, 도청자는 최종 비밀 색을 알 수 없습니다.
이 "섞기 쉽지만 분리는 어렵다"는 일방향성이 DH의 본질입니다.
🧮 수학적 원리 — 모듈러 지수와 이산 대수 문제
페인트 비유를 수학으로 옮기면 다음과 같습니다.
공개 파라미터 (모두가 아는 값)
큰 소수 p
p의 원시근 g (생성자, generator)
키 교환 절차
앨리스가 비밀값 a를 고르고, 공개값 A = g^a mod p 를 계산해 밥에게 보냅니다.
밥이 비밀값 b를 고르고, 공개값 B = g^b mod p 를 계산해 앨리스에게 보냅니다.
앨리스는 K = B^a mod p 를 계산합니다.
밥은 K = A^b mod p 를 계산합니다.
두 사람이 얻은 값은 모두 g^(ab) mod p 로 정확히 같습니다. 이것이 두 사람만 아는 공유 비밀 키(shared secret)가 됩니다.
도청자는 p, g, A, B를 모두 알 수 있지만, A = g^a mod p 에서 a를 역산해 내야만 합니다. 이 문제를 이산 대수 문제(Discrete Logarithm Problem, DLP)라고 하며, 충분히 큰 p(보통 2048비트 이상)에서는 현실적인 시간 안에 풀 수 없습니다.
# 교육용 예제 — 실제 운영에는 절대 사용 금지
p = 23 # 공개 소수 (실무에서는 2048비트 이상)
g = 5 # 공개 생성자
# 앨리스의 비밀값
a = 6
A = pow(g, a, p) # g^a mod p
print(f"앨리스가 공개하는 값 A = {A}")
# 밥의 비밀값
b = 15
B = pow(g, b, p) # g^b mod p
print(f"밥이 공개하는 값 B = {B}")
# 각자 공유 비밀 키 계산
K_alice = pow(B, a, p) # B^a mod p
K_bob = pow(A, b, p) # A^b mod p
print(f"앨리스의 공유 키: {K_alice}")
print(f"밥의 공유 키: {K_bob}")
assert K_alice == K_bob
실행하면 두 사람이 같은 키 2를 얻는 것을 확인할 수 있습니다.
실무에서는 직접 구현하지 말고 검증된 라이브러리를 쓰셔야 합니다. 다음은 cryptography 라이브러리로 안전한 DH 키 교환을 수행하는 예시입니다.
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# 1) 공개 파라미터 생성 (보통 서버가 1회 생성 후 재사용)
parameters = dh.generate_parameters(generator=2, key_size=2048)
# 2) 앨리스, 밥 각자 키 쌍 생성
alice_priv = parameters.generate_private_key()
bob_priv = parameters.generate_private_key()
# 3) 공개 키 교환 후 공유 비밀 도출
shared_alice = alice_priv.exchange(bob_priv.public_key())
shared_bob = bob_priv.exchange(alice_priv.public_key())
assert shared_alice == shared_bob
# 4) 원시 비밀은 그대로 쓰지 않고 KDF로 대칭 키를 유도
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"handshake data"
).derive(shared_alice)
여기서 한 가지 중요한 점은, DH가 만들어 낸 공유 비밀(raw shared secret)을 그대로 대칭 키로 사용하지 않는다는 것입니다. 분포가 균일하지 않을 수 있어 반드시 HKDF 같은 키 유도 함수(KDF)를 통해 정제한 뒤 사용합니다.
⚠️ 주의사항 — DH가 만능은 아니다
1. 중간자 공격(MITM)에 취약합니다
DH는 상대방이 누구인지 검증하지 않습니다. 공격자 말로리가 앨리스와 밥 사이에 끼어들면 다음과 같은 일이 벌어집니다.
앨리스 ↔ 말로리: 공유 키 K1 수립
말로리 ↔ 밥: 공유 키 K2 수립
말로리는 모든 메시지를 K1로 복호화 → 내용 확인/조작 → K2로 재암호화 후 전달
따라서 실무에서는 반드시 디지털 서명, 인증서(PKI), 사전 공유 키 등으로 상대방의 신원을 인증해야 합니다. TLS에서 서버 인증서가 그 역할을 하지요.
2. 약한 파라미터는 치명적입니다
2015년 발표된 Logjam 공격은 많은 서버가 512비트짜리 짧은 DH 파라미터를 쓰던 점을 노렸습니다. 또한 충분히 무작위한 비밀값 a, b를 사용하지 않으면 추측 공격에 노출됩니다. 최소 2048비트 이상의 안전한 소수 그룹(RFC 7919의 ffdhe 그룹 등)을 사용하시는 것이 안전합니다.
3. 정적 DH vs 임시 DH (DHE)
키 쌍을 한 번 만들어 계속 재사용하는 정적 DH는, 비밀 키가 한 번 유출되면 과거의 모든 통신이 복호화됩니다. 반면 매 세션마다 새 키 쌍을 생성하는 DHE(Ephemeral DH)는 순방향 비밀성(Forward Secrecy)을 제공합니다. TLS 1.3에서는 DHE 또는 ECDHE만 허용하는 것도 이 때문입니다.
4. 양자 컴퓨터에는 약합니다
이산 대수 문제는 쇼어 알고리즘으로 다항 시간에 풀립니다. 충분히 강력한 양자 컴퓨터가 등장하면 DH는 깨집니다. NIST가 추진 중인 포스트 양자 키 교환(PQC), 특히 ML-KEM(구 Kyber)과 DH를 결합한 하이브리드 방식이 차세대 표준으로 자리 잡고 있습니다.
🌐 ECDH — 더 작고, 더 빠르고, 더 강하게
타원곡선 위에서 같은 아이디어를 구현한 것이 ECDH(Elliptic Curve Diffie-Hellman)입니다.
항목
전통 DH
ECDH
기반 문제
이산 대수 (DLP)
타원곡선 이산 대수 (ECDLP)
동등 강도 키 크기
2048비트
256비트
연산 속도
느림
빠름
대역폭
큼
작음
같은 보안 강도를 훨씬 짧은 키로 달성할 수 있어, 현대 TLS 1.3, SSH, Signal 프로토콜 등 거의 모든 곳에서 ECDHE(임시 ECDH)가 표준으로 자리 잡았습니다.
✅ 정리 / 마무리
Diffie-Hellman은 공개 채널에서 비밀 키를 합의하게 해 주는 최초의 공개 키 알고리즘입니다.
핵심 원리는 g^(ab) mod p 라는 대칭적 결과와, 이산 대수 문제의 계산적 어려움입니다.
DH는 키 교환만 담당할 뿐, 상대방을 인증하지는 않습니다. 반드시 서명·인증서와 함께 써야 안전합니다.
실무에서는 임시 키를 사용하는 DHE/ECDHE가 표준이며, 이는 TLS의 순방향 비밀성을 책임집니다.
다음 단계로는 TLS 1.3 핸드셰이크, Signal 프로토콜의 X3DH, 그리고 양자 내성 키 교환인 ML-KEM(Kyber)을 살펴보시면 좋습니다.