간단히 요약하면: WireGuard는 TLS를 사용하지 않으며, OpenVPN이나 IPsec/IKEv2처럼 여러 번의 왕복 협상도 없습니다. Noise 프로토콜 프레임워크 — 구체적으로는 Noise_IK 패턴 — 에서 파생된 고정된, 독자적인 암호학적 핸드셰이크를 사용하며, 정확히 두 개의 메시지로 완료됩니다. 양쪽 피어는 서로의 장기 공개 키를 사전에 알고 있습니다(SSH 키가 구성되는 방식처럼 정적으로 설정됩니다). 따라서 핸드셰이크의 역할은 신원을 새롭게 발견하는 것이 아니라, 각자 대응하는 개인 키를 소유하고 있음을 상호 증명하고 전방 비밀성을 갖춘 새로운 대칭 세션 키를 유도하는 것입니다. 전체 트랜잭션은 각각 수백 바이트에 불과한 두 개의 UDP 데이터그램에 담을 수 있을 만큼 작습니다. TLS 핸드셰이크보다 빠르게 완료되며, 결과로 생성된 세션 키는 연결이 유지되는 동안 2분마다 교체됩니다.
핸드셰이크가 필요한 이유
두 컴퓨터가 자신들이 통제하지 못하는 네트워크 위에서 비공개로 통신하려 합니다. 근본적인 암호학적 문제는 이렇습니다. 무언가를 암호화하기 전에, 네트워크를 감청하는 공격자가 유도할 수 없는 공유 비밀을 합의해야 합니다. 또한 서로의 신원을 확인해야 합니다 — 그렇지 않으면 공격자가 상대방을 사칭해 피어 한쪽이 공격자에게 데이터를 암호화하도록 속일 수 있습니다.
핸드셰이크는 구조화된 교환을 통해 두 가지 문제를 모두 해결합니다:
- 상호 인증 — 각 피어는 상대방이 이미 신뢰하는 공개 키에 대응하는 개인 키를 소유하고 있음을 증명합니다. 어떤 피어도 개인 키 자체를 전송하지 않습니다. 오직 해당 키만이 생성할 수 있는 암호학적 연산을 수행함으로써 소유를 증명할 뿐입니다.
- 전방 비밀 키 합의 — 양쪽 피어는 이 특정 세션을 위해 새로운 임시("에피메럴") 키 쌍을 생성하고, 공개 절반을 교환한 다음, 장기 키와 결합하여 대칭 세션 키를 유도합니다. 에피메럴 키는 핸드셰이크 후 폐기됩니다. 나중에 장기 키가 도난당하더라도 과거 세션 키를 재구성할 수 없습니다. 이 특성을 전방 비밀성이라 하며, 미래에 키가 유출되더라도 과거 트래픽이 보호되는 이유입니다.
- 재전송 방지 설정 — 양쪽 피어는 공격자가 이전 트래픽을 캡처하여 나중에 재전송하는 것을 방지하는 카운터와 논스를 초기화합니다.
VPN 프로토콜마다 이를 구현하는 방식이 다릅니다. OpenVPN은 여러 번의 왕복, 인증서 유효성 검사, 다양한 설정 가능한 암호 스위트를 사용하는 TLS를 통해 처리합니다. IPsec/IKEv2는 유사한 복잡성을 가진 자체 협상 프로토콜을 갖고 있습니다. WireGuard는 두 개의 패킷으로, 설정 불가, 암호 스위트 협상도 없이 처리합니다 — 모든 WireGuard 핸드셰이크는 동일한 고정된 암호학적 기본 요소 세트를 사용합니다. 이 절충안은 의도적입니다. 복잡성이 낮을수록 공격 표면이 줄어들고 감사가 용이해지지만, 프로토콜 수준의 유연성은 전혀 없습니다.
Noise 프레임워크 — 무엇이며 WireGuard가 Noise_IK를 사용하는 이유
Noise 프로토콜 프레임워크는 암호학적 핸드셰이크를 구축하기 위한 명세입니다. Signal, WhatsApp 및 대부분의 현대 종단 간 암호화 메신저를 보호하는 Signal 프로토콜을 설계한 Trevor Perrin의 작업입니다. Noise는 단일 프로토콜이 아니라 구성 요소의 키트(핸드셰이크 "패턴", 암호학적 기본 요소, 유도 규칙)로, 프로토콜 설계자가 정확히 필요한 핸드셰이크 형태를 선택하고 메시지 내용과 제공되는 보증에 대한 정확한 명세를 얻을 수 있게 합니다.
Noise 핸드셰이크 패턴은 각 측이 사전에 알고 있는 내용을 나타내는 두 글자로 명명됩니다:
- N — 해당 측에 정적 키가 없습니다. 에피메럴 키만 사용됩니다. 익명입니다.
- K — 정적 키가 상대방에게 사전에 "알려져" 있습니다.
- X — 정적 키가 핸드셰이크 중에 전송됩니다.
- I — 정적 키가 첫 번째 메시지에서 "즉시" 전송됩니다.
Noise_IK는 이렇게 해석됩니다. 개시자는 자신의 정적 키를 즉시(첫 번째 메시지에서) 전송하고, 응답자의 정적 키는 개시자에게 사전에 알려져 있습니다. 이 패턴은 WireGuard의 사용 사례에 이상적입니다. 양쪽 피어가 실제로 서로의 정적 키를 사전에 알고 있기 때문입니다 — WireGuard 설정 파일에 구성됩니다. "IK" 선택은 전체 핸드셰이크가 두 개의 메시지로 완료되게 하며, 이는 상호 인증된 전방 비밀 키 교환에서 가능한 최솟값입니다.
Noise 프레임워크의 전체 명세는 noiseprotocol.org에 공개되어 있으며, 감사 가능하도록 작성되고 패턴 자체의 형식적 검증이 포함된 더 깔끔한 공개 암호학적 엔지니어링의 사례 중 하나입니다. WireGuard의 Noise_IK 채택은 프레임워크의 분석을 상속하기 위한 의도적인 설계 선택이었습니다.
패킷 1: 개시자 → 응답자
핸드셰이크는 개시자(여러분의 스마트폰, 노트북, 클라이언트 측)가 응답자(VPN 서버)와 통신하기로 결정할 때 시작됩니다. 이는 터널을 기다리는 트래픽이 큐에 있거나, 세션 키가 만료되어 재키가 필요할 때 발생합니다. 개시자는 다음을 포함하는 단일 UDP 패킷을 구성합니다:
- 발신자 인덱스 — 이 핸드셰이크 세션을 식별하기 위해 무작위로 선택된 32비트 정수. 동일한 피어 간의 동시 핸드셰이크를 구별하는 데 사용됩니다.
- 에피메럴 공개 키 — 새로 생성된 Curve25519 공개 키. 대응하는 개인 키는 개시자의 메모리에 보관됩니다. 이것이 Noise 패턴의 "E" — 도입되는 새 에피메럴 키입니다.
- 정적 공개 키(암호화됨) — 개시자의 장기 Curve25519 공개 키이지만, 에피메럴 교환에서 유도된 키로 암호화됩니다. 이것이 IK의 "I" 부분입니다 — 개시자의 정적 키가 첫 번째 메시지에서 전송되지만 수동적 관찰자로부터 보호됩니다.
- 타임스탬프(암호화됨) — TAI64N 형식의 타임스탬프, 암호화됨. 이것이 재전송 방지 앵커입니다. 응답자는 이미 이 피어로부터 수락한 타임스탬프보다 이전의 핸드셰이크를 거부합니다. 공격자가 유효한 핸드셰이크 메시지를 캡처하여 나중에 재전송하는 것을 방지합니다.
- MAC 필드 — 발신자가 응답자의 공개 키를 알고 있음을 증명하는 두 개의 짧은 인증자(mac1, mac2). mac2 필드는 WireGuard 논문에 설명된 쿠키-응답 DoS 완화 메커니즘에도 사용됩니다.
이 패킷이 증명하고 달성하는 것: 새로운 에피메럴 키를 포함함으로써, 개시자는 최종 세션 키의 절반에 커밋합니다. 타임스탬프와 응답자의 정적 키에 연결된 MAC를 포함함으로써, 개시자는 "적어도 응답자의 공개 키를 갖고 있다"는 것을 증명합니다 — 즉, 무작위 외부 공격자는 가짜 핸드셰이크 개시를 손쉽게 전송하여 서버 리소스를 낭비시킬 수 없습니다. 전체 패킷은 148바이트로, 단일 UDP 데이터그램에 쉽게 들어가며 절대 단편화되지 않습니다.
주목할 만한 한 가지 세부 사항: 개시자의 정적 공개 키가 포함되지만 암호화되어 있으므로, 네트워크를 수동적으로 감청하는 관찰자는 어떤 클라이언트가 연결하는지 식별할 수 없습니다. 누군가가 이 서버에 WireGuard 핸드셰이크를 개시하고 있다는 것은 볼 수 있지만, 누구인지는 알 수 없습니다. 이것은 개시자 신원 숨김이라 불리는 프라이버시 특성으로, 기본적으로 TLS가 제공하는 것보다 강력합니다.
패킷 2: 응답자 → 개시자
응답자는 개시 패킷을 수신하고, 개시자의 정적 공개 키를 복호화하여 피어 테이블에서 조회합니다. 피어가 알려져 있고 타임스탬프가 허용 가능한 경우(마지막으로 수락된 것보다 최신인 경우), 응답자는 자체 에피메럴 키 쌍을 생성하고 응답 패킷을 구성합니다:
- 발신자 인덱스 — 이 세션에 대해 응답자가 선택한 32비트 식별자.
- 수신자 인덱스 — 개시자로부터 에코된 값으로, 양측이 어느 세션인지 합의할 수 있게 합니다.
- 에피메럴 공개 키 — 응답자의 새로 생성된 에피메럴 공개 키.
- 빈 암호화 페이로드 — 응답자가 세션 키를 유도할 수 있음을 증명하는 인증되었지만 비어 있는 암호문. 이것이 암호학적 "당신의 메시지를 읽고 올바른 키를 계산했습니다" 신호입니다.
- MAC 필드 — 개시 패킷과 동일한 패턴.
개시자가 이 응답을 수신하면, 매칭되는 키 유도를 수행하고, 인증된 빈 페이로드가 유도된 키로 올바르게 복호화되는지 확인합니다. 이제 양측은 동일한 대칭 세션 키에 독립적으로 도달했습니다 — 그 키가 네트워크를 가로지르는 일 없이. 세션 키는 두 에피메럴 키(곧 파기될 예정) 및 두 정적 키 모두에 의존하기 때문에 완전한 전방 비밀성을 갖습니다.
응답 패킷은 92바이트입니다. 전체 핸드셰이크 — 두 패킷 합계 — 는 IP/UDP 헤더를 제외하고 240바이트이며, 대략 하나의 네트워크 RTT(각 방향 한 패킷)에서 완료되는 두 번의 UDP 왕복으로 이루어지고, 애플리케이션 트래픽을 암호화할 준비가 된 완전히 인증된 전방 비밀 세션을 생성합니다. 인증서 체인 유효성 검사도, 암호 스위트 협상도, 버전 비교도, 확장 목록도 없습니다 — TLS 핸드셰이크를 복잡하게 만드는 것들이 전혀 없습니다.
이후의 세션
핸드셰이크가 완료되면, 두 피어는 훨씬 간단한 형식의 데이터 패킷을 교환합니다. 각 데이터 패킷에는 다음이 포함됩니다:
- 수신자 인덱스 — 핸드셰이크 중에 수신자가 할당한 32비트 세션 ID.
- 카운터 — 전송되는 각 패킷마다 증가하는 64비트 논스. AEAD 논스로도 재전송 감지에도 사용됩니다.
- 암호화된 페이로드 — 세션 키와 카운터를 논스로 사용하여 ChaCha20-Poly1305로 암호화된 원래 IP 패킷.
ChaCha20-Poly1305는 AEAD(연관 데이터가 있는 인증 암호화) 구성입니다. 페이로드를 암호화하면서 동시에 변조를 감지하는 인증 태그를 생성합니다. 암호문에서 단 하나의 비트라도 뒤집히면 복호화가 실패하고 수신자는 패킷을 폐기합니다. "부드러운" 실패 모드가 없습니다 — 수정된 패킷은 잘못된 카운터나 잘못된 수신자 인덱스를 가진 패킷처럼 조용히 삭제됩니다.
수신자는 수락된 카운터 값의 슬라이딩 윈도우를 유지합니다. 윈도우 내에 있고 이전에 본 적 없는 카운터를 가진 패킷은 수락됩니다. 윈도우 아래에 있는(너무 오래된) 패킷이나 이미 본 패킷은 거부됩니다. 이를 통해 수신자가 이전에 본 모든 카운터를 기억할 필요 없이 재전송 방지를 제공합니다 — 작은 비트맵에 최근 것들만 유지하면 됩니다.
네트워크 관점에서 데이터 패킷은 페이로드 내용에 상관없이 동일하게 보입니다. 모두 동일한 서버 IP 및 포트로의 UDP 패킷으로, 유사한 크기(암호화된 페이로드는 기본 IP 패킷의 크기에 32바이트 오버헤드를 더한 것)를 가지며, 이들을 구별하는 관찰 가능한 패턴이 없습니다. SNI도, 인증서 교환도, TLS 핸드셰이크 후 데이터 구조도 없습니다 — 초기 두 패킷 핸드셰이크 이후에는 그냥 불투명한 UDP입니다.
재키 동작 — 2분마다 또는 2^60 패킷마다
WireGuard는 세션 키를 적극적으로 교체합니다. 기본 재키 정책: 현재 세션이 2분 동안 활성화되었거나 또는 2^60 패킷을 전송했을 때, 둘 중 먼저 도달하는 경우 새 핸드셰이크가 시작됩니다. 실제로는 시간 제한이 대부분의 세션에서 재키를 유발합니다 — 2^60 패킷은 실제 연결이 절대 도달하지 못하는 천문학적으로 큰 수입니다.
왜 2분인가요? 두 가지 이유가 있습니다. 첫째, 잦은 재키는 전방 비밀성을 향상시킵니다. 각 세션은 하나의 특정 에피메럴 키 쌍에서 유도된 키로 암호화됩니다. 세션이 짧을수록 단일 키를 통해 흐르는 데이터가 적어지므로, 이론적인 미래의 암호 분석 공격조차도 의미 있는 양의 트래픽을 읽으려면 많은 키를 해독해야 합니다. 둘째, 재키는 애플리케이션에 보이지 않습니다 — 새 핸드셰이크가 백그라운드에서 이루어지는 동안 이전 세션 키는 계속 데이터를 암호화하며, 전환이 완벽하게 이루어집니다. 양쪽 피어는 이전 또는 새 키로 암호화된 패킷 모두를 수락하는 작은 중첩 윈도우를 유지합니다.
재키는 타이머가 먼저 만료됨을 인지하는 피어 — 보통 개시자 측 — 에 의해 시작됩니다. 프로토콜 메시지는 위에 설명된 동일한 두 패킷 핸드셰이크입니다. 외부 관찰자의 관점에서는 2분마다 추가적인 UDP 패킷의 작은 폭발을 볼 수 있지만, 데이터 스트림 자체에서 관찰 가능한 변화는 없습니다.
핸드셰이크 단계 한눈에 보기
단계, 패킷, 작동하는 암호학적 기본 요소별로 분류된 전체 핸드셰이크:
| 단계 | 패킷 | 전송되는 내용 | 암호학적 기본 요소 |
|---|---|---|---|
| 1. 개시 | 개시자 → 응답자 (148바이트) | 발신자 인덱스, 에피메럴 공개 키, 암호화된 정적 키, 암호화된 타임스탬프, MAC | Curve25519 (ECDH), BLAKE2s (해시/MAC), ChaCha20-Poly1305 (AEAD), HKDF (키 유도) |
| 2. 응답 | 응답자 → 개시자 (92바이트) | 발신자 인덱스, 수신자 인덱스, 에피메럴 공개 키, 빈 암호화 페이로드(키 확인), MAC | Curve25519 (ECDH), BLAKE2s, ChaCha20-Poly1305, HKDF |
| 3. 데이터 세션 | 양방향, 무한히 (패킷당 32바이트 오버헤드) | 수신자 인덱스, 카운터, 암호화된 IP 패킷 | ChaCha20-Poly1305 (AEAD) |
| 4. 재키 (2분마다) | 1+2단계와 동일한 두 패킷 교환 | 새 에피메럴 키, 새 세션 키 유도 | 동일한 기본 요소 |
협상 불가능한 다섯 가지 암호학적 기본 요소가 전반에 걸쳐 사용됩니다: 타원 곡선 디피-헬만 키 합의를 위한 Curve25519, 대칭 스트림 암호화를 위한 ChaCha20, 인증 태그를 위한 Poly1305, 해시 함수와 MAC를 위한 BLAKE2s, 키 유도를 위한 HKDF. 이것이 "유연성 없음" 설계 선택입니다. 기본 요소를 하드코딩함으로써 프로토콜 표면이 크게 줄어들고 분석이 훨씬 용이해집니다. 단점은 어떤 기본 요소가 깨지면 전체 프로토콜에 새 버전이 필요하다는 것입니다 — 대역 내 업그레이드 방법이 없습니다. WireGuard가 거는 내기는 이 기본 요소들이 충분히 연구되어 있어 갑작스러운 붕괴가 unlikely하다는 것입니다.
잘못될 수 있는 것들
암호학적 핸드셰이크에는 알려진 실패 모드가 있습니다. 일부는 WireGuard에 적용되고 일부는 그렇지 않습니다. 이해할 가치가 있습니다:
재전송 공격
개시 패킷의 암호화된 타임스탬프는 재전송을 방지합니다. 공격자가 유효한 개시 패킷을 캡처하여 나중에 재전송하려 하면, 응답자는 이미 수락했거나(또는 마지막으로 수락한 것보다 오래된) 타임스탬프를 보고 패킷을 거부합니다. 이것이 WireGuard가 최신 핸드셰이크 타임스탬프에 대한 피어별 상태를 유지하는 이유 중 하나입니다 — 프로토콜이 이에 의존합니다.
장기 키 유출
공격자가 피어의 장기 개인 키를 도용하면, 미래의 핸드셰이크에서 해당 피어를 사칭할 수 있습니다. 그러나 과거 세션은 복호화할 수 없습니다 — 해당 세션 키를 유도한 에피메럴 키들은 각 핸드셰이크 후 파기되었기 때문입니다. 이것이 전방 비밀성 보장입니다. 지속적인 위험에 대한 완화책은 장기 키를 교체하는 것입니다(새 쌍을 생성하고 피어에게 새 공개 키를 배포) — 오래된 키를 무효화하는 프로토콜 수준 메커니즘이 없으므로, 이는 대역 외에서 이루어져야 합니다.
NAT 타임아웃과 조용한 연결 단절
WireGuard는 비연결성 UDP 위에서 실행됩니다. 네트워크 주소 변환기(홈 라우터 또는 ISP 네트워크에서)는 몇 분간의 비활성 후 UDP 흐름 항목을 삭제하는 경우가 있습니다. 이때 응답자의 응답 패킷이 더 이상 개시자에게 도달할 수 없습니다 — NAT가 매핑을 잊어버렸기 때문입니다. 완화책은 킵얼라이브 설정입니다(종종 25초로 설정), 이는 개시자에서 응답자로 소형 하트비트 패킷을 전송하여 NAT 매핑을 유지합니다. 킵얼라이브 없이는 유휴 연결이 조용히 단절되어 복원을 위해 새 핸드셰이크가 필요할 수 있습니다.
핸드셰이크 플러드를 통한 서비스 거부
공격받는 응답자는 많은 가짜 핸드셰이크 개시를 수신할 수 있으며, 각각은 처리하기 위한 Curve25519 연산이 필요합니다. WireGuard는 쿠키-응답 메커니즘으로 이를 완화합니다. 응답자가 부하 하에 있을 때, 응답자가 전체 핸드셰이크에 커밋하기 전에 개시자가 소형 작업 증명 스타일의 계산을 수행하도록 요구할 수 있습니다. 핸드셰이크 패킷의 mac2 필드가 이 메커니즘을 담습니다. DoS 저항성이 완벽하지는 않습니다 — 어떤 프로토콜도 완벽할 수 없습니다 — 하지만 예를 들어 인증되지 않은 TCP 기반 서비스보다 훨씬 플러드하기 어렵습니다.
VPN 사용자에게 실질적으로 중요한 이유
핸드셰이크 아키텍처는 이전 VPN 프로토콜과 비교하여 구체적이고 사용자에게 보이는 이점을 가집니다:
- 클라이언트 측 TLS 핸드셰이크 없음 — WireGuard는 인증서를 유효성 검사하지 않고, CA에 체인하지 않으며, 시스템 트러스트 저장소에 의존하지 않습니다. 이는 TLS 기반 VPN에 영향을 미치는 전체 공격 클래스(유출된 CA, 잘못 발급된 인증서)를 제거합니다.
- 상태 연결 없음 — 프로토콜이 UDP 위에서 실행되며 비연결성입니다. 네트워크가 변경될 때 깨질 TCP 스타일 상태 머신이 없습니다. Wi-Fi에서 셀룰러로 전환하는 로밍 기기는 아무것도 재수립할 필요가 없습니다. 새 IP 주소에서 패킷을 전송하기만 하면 응답자가 수락합니다.
- 즉각적인 로밍 — 연결이 소스 IP 주소가 아닌 암호학적 키로 식별되기 때문에, 응답자는 올바른 암호학적 인증을 생성하는 어떤 소스 IP에서든 패킷을 수락합니다. 이것이 WireGuard가 기본 IP가 변경될 때 종종 재협상이 필요한 OpenVPN이나 IPsec보다 모바일 네트워크 전환을 훨씬 잘 처리하는 이유입니다.
- 작은 공격 표면 — 참조 구현은 약 4,000줄의 코드입니다. OpenSSL은 500,000줄이 넘습니다. 이것이 공정한 비교는 아니지만(다른 양의 작업을 수행합니다), 함의는 실질적입니다. 코드가 작을수록 감사하기 쉽고, 형식적으로 분석하기 쉬우며, 취약점으로 변할 수 있는 구현 버그의 기회가 적습니다.
- 예측 가능한 성능 — 모든 핸드셰이크는 동일한 키 크기에서 동일한 기본 요소를 사용합니다. 협상된 암호 스위트에 따른 빠른 경로/느린 경로 분기가 없습니다. 핸드셰이크는 누구에게 연결하든 동일한 하드웨어에서 거의 동일한 시간이 걸립니다.
대부분의 사용자에게 대체된 이전 프로토콜과의 절충점 비교는 WireGuard vs OpenVPN에 대한 저희 글을 참조하세요. 실용적인 성능 및 보안 차이를 다룹니다. VPN의 터널링 선택이 앱에 미치는 영향에 대해서는 스플릿 터널링이란 무엇인가를 참조하세요.
Casper's Cloak는 모든 클라이언트 연결에 WireGuard를 사용하고, Linux 엔드포인트에서 공식 커널 모드 구현을 실행하며, iOS, Android, macOS에서 표준 사용자 공간 클라이언트(wireguard-go 참조 구현 기반)를 제공합니다. 위에 설명된 핸드셰이크는 앱을 켤 때 정확히 일어나는 일입니다. 동일한 Noise_IK 패턴, 동일한 Curve25519/ChaCha20-Poly1305/BLAKE2s/HKDF 기본 요소 세트, 동일한 2분 재키, 동일한 240바이트 핸드셰이크. 위협 방어 기능은 WireGuard 터널 위에 DNS 수준 필터링을 추가합니다 — 터널은 전송을 처리하고, 필터는 목적지 정책을 처리합니다.
전체 프로토콜 명세는 Jason Donenfeld의 원래 WireGuard 백서가 권위 있는 참고 자료입니다: wireguard.com/papers/wireguard.pdf. 논문은 암호학적 구성, 위협 모델, 형식 분석, 구현 근거를 다룹니다. 더 읽기 쉬운 현대 프로토콜 설계 글 중 하나입니다 — 소스에서 설계 절충점을 이해하려면 한 시간 정도 훑어볼 가치가 있습니다.
결론: WireGuard 핸드셰이크는 두 개의 패킷으로 이루어지며, 실제로 100밀리초 이내에 완료되고, 양쪽 피어를 상호 인증하며, 전방 비밀 세션 키를 유도하고, 설계에 의해 모든 일반적인 공격 패턴(재전송, 다운그레이드, 신원 공개, DoS)에 저항합니다. 엄격하고, 고정되어 있으며, 독자적인 프로토콜이며, 그 독자성이 바로 핵심입니다.