DTLS + SRTP — 미디어 암호화의 실제

kodeholic·2026년 3월 27일

SFU 해부학

목록 보기
3/5

SFU 해부학 시리즈 #3

지난 편에서 ICE-Lite로 클라이언트와 SFU 사이에 UDP 연결을 수립했다. 파이프가 뚫린 것이다.

그런데 이 파이프로 영상과 음성을 날것 그대로 보내면 어떻게 될까? 같은 와이파이 네트워크에 있는 누구나 패킷을 캡처해서 내용을 볼 수 있다. 카페에서 화상회의를 하면 옆자리 사람이 내 화면을 엿볼 수 있다는 뜻이다.

WebRTC는 미디어 암호화를 선택이 아닌 필수로 규정한다(RFC 8827). 브라우저가 암호화 없는 미디어 전송을 아예 허용하지 않는다. 이 암호화가 DTLS와 SRTP의 조합으로 이루어진다.


1. 왜 TLS가 아니라 DTLS인가

웹에서 암호화라면 TLS가 떠오른다. HTTPS가 바로 HTTP + TLS다. 검증된 기술이니 그대로 쓰면 될 것 같다.

문제는 TLS가 TCP 위에서 동작한다는 점이다. TLS는 바이트 스트림이 순서대로 빠짐없이 도착한다는 TCP의 보장을 전제로 설계되었다. 패킷 하나가 유실되면 뒤따르는 모든 패킷이 대기한다(Head-of-Line Blocking).

WebRTC 미디어는 UDP로 전송된다. 실시간 영상에서는 패킷 하나를 기다리느라 전체가 멈추는 것보다, 그 패킷을 버리고 다음 프레임으로 넘어가는 게 낫다. UDP는 순서 보장도, 재전송도 하지 않는다. TLS를 그대로 올릴 수 없다.

DTLS(Datagram Transport Layer Security)는 이 문제를 해결한다. TLS의 암호화 메커니즘을 그대로 가져오되, UDP 위에서 동작하도록 수정한 프로토콜이다. 핵심 차이는 간단하다:

  • 패킷이 유실되어도 나머지가 멈추지 않는다
  • 핸드셰이크 메시지에 순서 번호와 재전송 타이머가 있다 (UDP는 순서를 보장하지 않으니까)
  • 암호화 자체의 강도는 TLS와 동일하다

정리하면: DTLS = TLS의 암호화 + UDP의 비순차 허용. "UDP 위의 TLS"라고 생각하면 된다.


2. DTLS 핸드셰이크 — 키를 교환하는 과정

ICE로 UDP 연결이 수립되면, 그 위에서 바로 DTLS 핸드셰이크가 시작된다. 목적은 하나다. 양쪽이 암호화에 쓸 키를 안전하게 합의하는 것.

  [클라이언트]                         [SFU]
       |                                |
       |------- ClientHello ----------->|   1. "나는 이런 암호화를 지원해"
       |<------ ServerHello ------------|   2. "이걸로 하자"
       |<------ Certificate ------------|   3. 서버 인증서 전달
       |<------ ServerHelloDone --------|
       |                                |
       |------- Certificate ----------->|   4. 클라이언트 인증서 전달
       |------- ClientKeyExchange ----->|   5. 키 교환 재료 전달
       |------- Finished -------------->|   6. "내 쪽 준비 완료"
       |<------ Finished ---------------|   7. "서버도 완료"
       |                                |
       |====== 암호화 채널 수립 =========|
       |                                |
       |  이후: SRTP 키 추출 (DTLS-SRTP)|

웹 브라우저에서 HTTPS 사이트에 접속할 때 일어나는 TLS 핸드셰이크와 거의 동일한 흐름이다. 차이는 이게 UDP 위에서 일어난다는 것, 그리고 핸드셰이크가 끝나면 데이터를 직접 DTLS로 보내는 게 아니라, SRTP 암호화에 쓸 키를 뽑아낸다는 것이다.

이 키 추출 방식을 DTLS-SRTP(RFC 5764)라고 한다. DTLS 핸드셰이크에서 합의된 마스터 시크릿으로부터 SRTP 암호화 키와 인증 키를 유도한다. 키 교환은 DTLS가 하고, 실제 미디어 보호는 SRTP가 한다. 역할 분담이다.


3. SRTP — 미디어 패킷을 보호하는 방법

키가 준비되었다. 이제 실제 미디어 패킷을 암호화할 차례다.

RTP(Real-time Transport Protocol)는 미디어 전송의 표준 프로토콜이다. 영상 프레임, 음성 샘플이 RTP 패킷에 담겨서 전송된다. SRTP(Secure RTP)는 이 RTP 패킷을 암호화한 버전이다.

SRTP의 구조에서 중요한 점이 하나 있다. 헤더는 암호화하지 않고, 페이로드만 암호화한다.

  일반 RTP 패킷:
  +------------------+----------------------+
  |  RTP Header      |  Payload (영상/음성)  |
  |  (평문)          |  (평문)               |
  +------------------+----------------------+

  SRTP 패킷:
  +------------------+----------------------+-----------+
  |  RTP Header      |  Payload (영상/음성)  |  Auth Tag |
  |  (평문)          |  (암호화됨)           |  (무결성) |
  +------------------+----------------------+-----------+

  ※ Header: SSRC, 시퀀스 번호, 타임스탬프, 페이로드 타입 등
  ※ Auth Tag: 헤더+페이로드 전체의 무결성을 검증하는 값

왜 헤더를 평문으로 두는가? 이유는 실용적이다:

  • 라우팅 판단이 복호화보다 먼저다. SFU는 하나의 포트로 모든 클라이언트의 패킷을 받는다. 패킷이 도착하면 복호화하기 전에 "이 패킷이 누구의 스트림인가"를 먼저 판단해야 한다. 헤더의 SSRC가 평문이니까 복호화 없이 즉시 식별할 수 있다. 헤더까지 암호화되면, 식별을 위해 먼저 복호화가 필요한데 어떤 키로 복호화할지 모르는 — 닭과 달걀 문제가 생긴다.
  • 품질 관리에 필요하다. 시퀀스 번호로 패킷 유실을 감지하고, 타임스탬프로 지터를 계산한다. 이 정보는 암호화 없이도 접근 가능해야 한다.
  • Auth Tag가 보호한다. 헤더가 평문이라고 보호되지 않는 건 아니다. Auth Tag는 헤더를 포함한 전체 패킷의 무결성을 보장한다. 누군가 헤더를 조작하면 Auth Tag 검증이 실패한다.

정리하면: 영상/음성 내용은 절대 볼 수 없고, 메타데이터(누가 보내고, 몇 번째 패킷인지)는 볼 수 있되 SRTP 키를 모르는 제3자는 조작할 수 없다. SFU처럼 키를 가진 종단점은 헤더를 변경하고 Auth Tag를 다시 생성할 수 있다. 이게 SRTP의 보안 모델이다.


4. SFU에서 왜 까다로운가

P2P에서는 간단하다. 양쪽이 DTLS 핸드셰이크를 한 번 하고, 그 키로 SRTP 암호화/복호화를 한다. 연결 하나, 키 하나.

SFU는 다르다. SFU는 모든 클라이언트와 각각 별도의 DTLS 세션을 유지한다.

  [Client A] <=== DTLS #1 ===> +=========+
  [Client B] <=== DTLS #2 ===> |   SFU   |
  [Client C] <=== DTLS #3 ===> |         |
  [Client D] <=== DTLS #4 ===> +=========+

  ※ 4명이면 DTLS 세션 4개, SRTP 키 4쌍
  ※ 30명이면 DTLS 세션 30개, SRTP 키 30쌍

Client A가 보낸 SRTP 패킷을 B, C, D에게 전달하려면:

  1. A의 SRTP 키로 복호화 (A → SFU 구간의 암호화 해제)
  2. B의 SRTP 키로 재암호화 후 B에게 전송
  3. C의 SRTP 키로 재암호화 후 C에게 전송
  4. D의 SRTP 키로 재암호화 후 D에게 전송
  A의 영상 패킷 흐름:

  [A] --SRTP(키A)--> [SFU] --복호화--> [평문 RTP]
                                          |
                       +--SRTP(키B)-----> [B]
                       +--SRTP(키C)-----> [C]
                       +--SRTP(키D)-----> [D]

  ※ SFU 내부에서 평문 RTP가 잠깐 존재한다
  ※ 하지만 미디어를 디코딩(H.264 → 영상)하지는 않는다

SFU는 미디어 내용을 "보지" 않는다. 암호화를 풀어서 RTP 패킷을 꺼내지만, 그 RTP 패킷을 디코딩해서 영상으로 만들지는 않는다. 봉투를 열어서 다른 봉투에 넣는 것이지, 편지를 읽는 게 아니다.

이 과정이 SFU 구현에서 까다로운 이유:

  • 키 관리: 참여자 N명이면 SRTP 키 N쌍을 관리해야 한다. 참여자가 들어오고 나갈 때마다 키가 생기고 사라진다.
  • CPU 비용: 복호화 + 재암호화를 모든 패킷에 대해 수행한다. MCU처럼 디코딩/인코딩을 하는 것보다는 훨씬 가볍지만, 패킷 수가 많으면 무시할 수 없다. AES-128-CTR 한 번에 수 나노초지만, 초당 수만 패킷이 지나간다.
  • DTLS 핸드셰이크 동시 처리: 여러 클라이언트가 동시에 입장하면 핸드셰이크가 동시에 진행된다. 핸드셰이크는 여러 라운드트립이 필요한 과정이라 비동기 처리가 필수다.

한눈에 비교

항목P2PSFU
DTLS 세션 수1개 (상대방과)N개 (참여자 수만큼)
SRTP 키1쌍N쌍
복호화/재암호화없음매 패킷마다
미디어 내용 접근양쪽만 가능SFU도 RTP 레벨 접근 (디코딩은 안 함)
핸드셰이크 시점연결 시 1회참여자 입장마다
키 관리 복잡도단순참여자 입퇴장에 따라 동적

암호화는 협상할 수 없다

WebRTC에서 DTLS + SRTP는 선택사항이 아니다. 브라우저가 강제한다. 암호화 없는 RTP 전송을 시도하면 브라우저가 거부한다.

이건 SFU 개발자에게 피할 수 없는 숙제다. ICE-Lite로 연결을 수립하고, DTLS 핸드셰이크로 키를 교환하고, SRTP로 모든 패킷을 암호화/복호화해야 한다. 이 세 단계를 전부 구현해야 클라이언트의 첫 번째 영상 프레임이 상대방에게 도달한다.

지금까지 SFU의 연결 과정을 따라왔다. ICE-Lite로 UDP 파이프를 뚫고, DTLS로 키를 교환하고, SRTP로 미디어를 보호한다. 다음 편에서는 이 과정 전체를 조율하는 시그널링, 그리고 왜 SFU에서 전통적인 SDP 교환 대신 다른 방식을 택하는지를 다룬다.

다음 편: [SFU 해부학 #4] SDP-Free 시그널링 — SDP를 버린 이유


참고 자료


이 글은 OxLens — Rust로 만드는 경량 SFU 서버 프로젝트를 개발하면서 배운 것들을 정리한 시리즈입니다.

profile
Creator of 0xLENS — the world's first AI-native, Rust-built SFU with real-time PTT Floor Control. No fork. No framework. Just Rust and resolve.

0개의 댓글