보안

[보안] TLS 키 교환의 비밀 - 대칭키와 비대칭키는 어떻게 협력할까

jmineekim 2026. 5. 19. 10:00

지난 글에서 TLS 핸드셰이크의 전체 흐름을 훑었는데요, 그때 "키 교환" 단계는 한 줄로 지나갔습니다. 사실 이 키 교환이야말로 TLS에서 가장 짜릿한 부분입니다. 서로 한 번도 만난 적 없는 두 사람이, 누가 옆에서 패킷을 다 보고 있는 상황에서, 어떻게 둘만 아는 비밀 키를 만들어낼까요? 마술 같지만 수학이 만든 일입니다.

저도 처음 이 원리를 이해했을 때 "아니 이게 진짜 되네?" 싶었어요. 오늘은 TLS 안에서 대칭키와 비대칭키가 어떻게 역할을 나눠 가지는지, 그리고 실제 키 교환이 어떤 식으로 이뤄지는지 한 단계씩 풀어보겠습니다.

개념 / 원리

대칭키와 비대칭키, 빠른 비교

항목 대칭키 비대칭키
키 개수 1개 (양쪽 공유) 2개 (공개키 + 개인키)
속도 매우 빠름 일반적으로 수백 배 느림
약점 키를 어떻게 안전하게 나눠 갖나? 느려서 대용량 통신엔 부적합
대표 알고리즘 AES, ChaCha20 RSA, ECDSA, ECDH

이걸 보면 답이 보입니다. 둘은 경쟁자가 아니라 분업 관계예요.

TLS의 하이브리드 전략

TLS는 둘을 똑똑하게 섞어 씁니다.

  • 비대칭키: 처음에 "세션 키"를 안전하게 만들거나 교환하는 데만 사용.
  • 대칭키: 일단 세션 키가 만들어지면, 그 뒤 실제 데이터는 전부 대칭키로 암호화.

비유하자면 비대칭키는 "금고 열쇠를 안전하게 전달하는 봉투"이고, 대칭키는 "그 금고로 매일 빠르게 물건을 주고받는 도구"입니다. 봉투는 한 번만 쓰고, 일상 작업은 빠른 도구로.

키 교환의 두 가지 방식

TLS 키 교환은 크게 두 갈래로 발전해 왔습니다.

  1. RSA 키 교환 (옛날 방식, TLS 1.2까지): 클라이언트가 "pre-master secret"을 생성 → 서버의 RSA 공개키로 암호화 → 서버에 전송. 서버만 자기 개인키로 풀 수 있음.
  2. (EC)DHE 키 교환 (현대 방식, TLS 1.3 필수): 클라이언트와 서버가 각자 임시 키 쌍을 만들고 공개값만 교환. 수학적 트릭으로 양쪽이 동일한 비밀 값을 도출. 개인키가 유출돼도 과거 트래픽 복호화 불가(Forward Secrecy).

RSA 방식은 서버 개인키가 한 번 유출되면 그동안 캡처돼 있던 모든 트래픽이 복호화됩니다. 그래서 TLS 1.3은 RSA 키 교환을 아예 제거했어요.

실전 예제

1) ECDHE 키 교환, 한 문단으로

ECDHE를 풀면 이렇습니다.

1. 양쪽이 같은 타원곡선 파라미터 G에 합의 (Cipher suite에 들어 있음)
2. 클라이언트: 난수 a 생성 → 공개값 A = a × G 계산 → A 전송
3. 서버:     난수 b 생성 → 공개값 B = b × G 계산 → B 전송
4. 클라이언트: a × B 계산 = a × b × G   (공유 비밀)
5. 서버:     b × A 계산 = b × a × G   (같은 값!)
6. 이 값으로 KDF를 돌려 세션 키(AES 키 등) 생성

핵심은 A와 B는 공개돼도 a, b를 역으로 알아내기가 (실용적으로) 불가능하다는 점입니다. 이게 타원곡선 이산로그 문제예요. 도청자는 A, B를 다 보더라도 a × b × G는 못 만듭니다.

2) 협상된 키 교환 방식 확인

내가 접속한 서버가 어떤 키 교환을 쓰는지 openssl s_client로 바로 볼 수 있습니다.

openssl s_client -connect www.google.com:443 -servername www.google.com </dev/null 2>&1 \
  | grep -E "Cipher|Server Temp Key|Protocol"

출력 예시는 보통 이렇게 나옵니다.

Protocol  : TLSv1.3
Cipher    : TLS_AES_256_GCM_SHA384
Server Temp Key: X25519, 253 bits
  • TLS_AES_256_GCM_SHA384: 대칭키 알고리즘은 AES-256-GCM.
  • Server Temp Key: X25519: 키 교환은 X25519 곡선 기반 ECDHE. "Temp"라는 단어가 임시 키 쌍(=ephemeral=Forward Secrecy)이라는 의미입니다.

3) 서버가 지원하는 키 교환 점검

nmap --script ssl-enum-ciphers -p 443 www.google.com

출력 안에 cipher마다 키 교환 방식이 표시됩니다. ecdh_x25519, ecdh_secp256r1 같은 게 보이면 ECDHE 기반이라는 뜻이에요. 만약 키 교환 컬럼에 rsa가 떡하니 있다면 PFS가 깨지는 옛날 cipher가 살아 있다는 신호.

4) nginx에서 ECDHE만 허용하기

운영 서버라면 설정 한 줄로 확실히 못 박을 수 있습니다.

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20;
ssl_ecdh_curve X25519:secp384r1:prime256v1;

ssl_ciphers에서 ECDHE만 남기면 RSA 키 교환은 자연스레 차단됩니다.

자주 하는 실수

  • "RSA 인증서니까 RSA 키 교환"이라고 오해: 인증서 알고리즘과 키 교환 알고리즘은 별개입니다. RSA 인증서를 써도 ECDHE 키 교환은 얼마든지 가능해요. 인증서는 "이 서버가 진짜인지"를 증명하는 신원증, 키 교환은 그것과 별개로 매 접속마다 새로 합니다.
  • Forward Secrecy를 끄고 운영: 옛날 클라이언트 호환성 때문에 ssl_ciphersAES128-SHA 같은 비(非)ECDHE 항목을 넣어두는 경우가 있습니다. 그러면 서버 개인키가 한 번 유출되는 순간 과거 트래픽이 다 풀립니다.
  • 세션 키와 인증서 개인키 혼동: 세션 키는 매 접속마다 새로 만들어지고 잠깐 살다 사라집니다. 인증서 개인키는 1년·2년 그대로죠. 둘은 수명도 역할도 완전히 다릅니다.
  • 곡선 선택을 기본값에 방치: 최신 OpenSSL에서는 X25519가 기본인데, 옛날 OpenJDK·라이브러리는 secp256r1만 잡고 있을 수 있습니다. 모바일이나 임베디드에서 호환성 문제가 터지면 곡선 목록부터 확인.
  • CDN 뒤 origin 점검 누락: CDN 뒤에서는 클라이언트가 보는 cipher가 CDN과 협상한 것이지 origin 서버와 협상한 게 아닙니다. origin까지 PFS인지 따로 확인이 필요해요.

저는 예전에 한 시스템에서 "트래픽 복호화가 가능해야 한다"는 요구가 있어서 RSA 키 교환을 일부러 켜둔 적이 있는데, 보안 점검에서 그 자리에서 지적당했습니다. 복호화가 정말 필요하면 차라리 정식 TLS 인터셉트 장비를 두는 게 맞아요.

마무리

정리하면 세 줄입니다.

  • 대칭키는 빠르고 비대칭키는 안전한 키 분배가 가능, TLS는 둘을 섞어 쓴다.
  • 현대 TLS는 ECDHE로 매 접속마다 임시 키 쌍을 만들고, 공개값만 교환해 같은 비밀을 도출한다 (Forward Secrecy).
  • 운영에서는 Server Temp Key가 잡히는지, cipher 목록에 RSA 키 교환이 안 섞여 있는지를 보면 된다.
반응형