在 WebRTC 中的作用
DTLS(Datagram Transport Layer Security)是 TLS 的 UDP 版本,在 WebRTC 中用于:
- 安全协商加密密钥
- 对等验证(基于 X.509 证书 + fingerprint)
- 为 SRTP/SRTCP 提供密钥材料
WebRTC 不直接用 DTLS 传输音视频,而是通过它协商出密钥后,用于 SRTP/SRTCP 加密 RTP/RTCP 流。
与 SRTP 的关系
在 WebRTC 中,音视频数据加密流程如下:
[RTP/RTCP] ─► SRTP/SRTCP ─► UDP Socket▲Key 来源于 DTLS
- DTLS:协商密钥(SDES 替代方案)
- SRTP:使用 DTLS 派生出的密钥加密 RTP
- SRTCP:同理用于 RTCP 加密
DTLS 本质上是提供 密钥协商、身份认证、消息完整性保护。
握手流程详解
流程图
Client Server| ------ ClientHello ------> || <--- HelloVerifyRequest -- || ------ ClientHello ------> | (带 cookie)| <-------- ServerHello -----|| <----- Certificate --------|| <- ServerKeyExchange (可选)| <- CertificateRequest (可选)| <---- ServerHelloDone -----|| ---- Certificate (可选) -->|| ---- ClientKeyExchange --> || ---- CertificateVerify --> || ---- ChangeCipherSpec --> || ---- Finished -----------> || <--- ChangeCipherSpec ---- || <-------- Finished --------|
ClientHello(第一次)
客户端发起握手,发送:
- 协议版本(如 DTLS 1.2)
- 随机数(Random)
- 支持的加密套件(cipher suites)
- 会话 ID(Session ID)
- 扩展(如 SNI)
HelloVerifyRequest(服务器防攻击措施)
- 目的:防止 DoS 攻击(攻击者伪造 IP,消耗服务器资源)
- 内容:服务端返回一个
cookie
给客户端,要求客户端再发送一次 ClientHello 并带上这个 cookie - 此时没有任何状态被保留(无状态机制)
ClientHello(第二次,带 Cookie)
- 客户端重新发起 ClientHello,并附上
cookie
- 服务端验证
cookie
成功后,进入真正的握手过程
ServerHello
- 服务端选择协议版本、加密算法
- 提供其
Random
、Session ID、压缩方法、扩展信息
Certificate(服务端证书)
- 服务端发送其证书(如 X.509),用于客户端验证其身份
ServerKeyExchange(可选)
- 如果所选加密套件是临时密钥交换(如 ECDHE),会发送这个报文,包含公钥参数等
CertificateRequest(可选)
- 如果需要客户端身份认证,服务端请求客户端提供证书
ServerHelloDone
- 服务端完成所有 hello 消息,提示客户端可以继续下一步
Certificate(客户端,若服务端请求)
- 客户端发送证书用于身份认证(如 Mutual TLS)
ClientKeyExchange
- 客户端发送密钥交换材料(如 DH 公钥、RSA 加密的 pre-master secret)
CertificateVerify(可选)
- 如果客户端提供了证书,此报文包含对前面握手消息的签名,用于验证客户端拥有私钥
ChangeCipherSpec
- 客户端通知服务端:接下来所有数据都将使用协商好的加密套件加密
Finished
- 客户端发送已加密的
Finished
消息,包含所有握手摘要的摘要(验证握手完整性)
服务端 ChangeCipherSpec + Finished
- 服务端也切换加密,并发送加密的
Finished
消息
特有的机制
特性 | 说明 |
---|---|
消息序列号(Message Sequence) | 每条握手消息都有编号,用于乱序重组 |
重传机制 | 如果对方没有应答,会重传握手消息(无 ACK) |
记录层编号(Epoch/Sequence Number) | 每个阶段有不同的 epoch 和 seq ,用于包重放保护 |
HelloVerifyRequest | 防止 IP 伪造攻击,DTLS 专属机制 |
重传机制(Retransmission)
- 为什么需要?
UDP 不保证报文到达和顺序,DTLS 握手报文可能丢失,必须实现重传保证握手完成。 - 机制内容:
- DTLS 对握手消息做序列号标记;
- 如果客户端或服务器在超时时间内未收到预期消息,会重传之前的握手消息;
- 重传报文必须被对端识别为重复消息并正确处理,不导致状态错误。
消息序列号(Message Sequence Number)
- 用途:
防止握手消息乱序,保证握手阶段状态同步。 - 特点:
- 每个握手消息分配序列号,递增;
- 接收端按序列号处理消息,忽略重复或乱序消息。
握手消息片段化(Fragmentation)
- 为什么需要?
UDP 报文大小有限,握手消息可能大于单个 UDP 报文大小。 - 机制内容:
- 握手消息可以被分割成多个片段(Fragment)发送;
- 接收端负责重组片段,直到完整消息;
- 每个片段带有片段偏移和长度信息。
Cookie 机制(HelloVerifyRequest)
- 目的:
防止DoS 攻击(反射放大攻击),避免服务器资源被恶意请求耗尽。 - 流程:
- 客户端第一次发送
ClientHello
,服务器不直接响应握手,而是回复一个带有 Cookie 的HelloVerifyRequest
; - 客户端必须带上该 Cookie 重新发送
ClientHello
; - 服务器验证 Cookie 合法后才继续握手流程。
- 客户端第一次发送
序列号和重放保护(Replay Protection)
- 内容:
- DTLS 为每条记录分配序列号(Record Sequence Number),确保记录不会被重放;
- 接收端检测重复序列号,丢弃重放包;
- 与 TLS 不同的是,DTLS 要在无连接环境下实现此功能。
无连接的记录层(Record Layer)
- DTLS 使用和 TLS 类似的记录层结构,但消息通过 UDP 发送,没有连接状态;
- 每条记录带有协议版本、内容类型、长度和序列号字段。
时间戳和定时器
- DTLS 需要在握手时设置超时定时器,管理重传和超时处理;
- 这在 TLS(基于 TCP)中不需要,因为 TCP 已经保证了可靠传输。
支持 MTU 探测与路径 MTU 发现
- 由于 UDP 报文大小限制,DTLS 可以基于握手分片检测 MTU,以防止 IP 层分片导致性能下降。
Fingerprint(证书指纹)
DTLS Fingerprint 是对参与 DTLS 握手的 X.509 证书进行哈希计算(例如 SHA-256),生成的一串唯一字符串。
WebRTC 中不会使用 CA 机构来验证证书,而是通过交换证书指纹(fingerprint)来验证对等端。
- SDP 协商中携带 fingerprint
- 格式示例:
a=fingerprint:sha-256 7F:B8:33:...:9D
验证流程:
- 浏览器生成 DTLS 证书(X.509);
- 提取 fingerprint,加入 SDP;
- 对端在接收 DTLS 握手证书时,校验是否与 fingerprint 匹配;
- 若不匹配,则拒绝连接。
这确保了对端身份未被中间人篡改。
OpenSSL DTLS
#include <openssl/ssl.h>
#include <openssl/err.h>SSL_CTX *init_dtls_server_ctx() {const SSL_METHOD *method = DTLS_server_method();SSL_CTX *ctx = SSL_CTX_new(method);// 加载服务器证书和私钥SSL_CTX_use_certificate_file(ctx, "server_cert.pem", SSL_FILETYPE_PEM);SSL_CTX_use_PrivateKey_file(ctx, "server_key.pem", SSL_FILETYPE_PEM);// 设置握手重传超时和最大重试次数// DTLS 有内置超时机制,OpenSSL自动处理重传// 不用手动实现重传逻辑return ctx;
}void dtls_handshake(SSL *ssl, BIO *bio) {// 设置BIO为无连接UDP模式SSL_set_bio(ssl, bio, bio);// 阻塞握手直到成功或失败int ret = SSL_accept(ssl);if (ret <= 0) {int err = SSL_get_error(ssl, ret);if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {// 需要继续读写,可能是等待重传// 事件循环继续} else {// 握手失败处理}}// 握手成功后,调用SSL_read/SSL_write进行加密数据收发
}
DTLS 与 TLS 的关键区别
项目 | TLS | DTLS |
---|---|---|
传输层 | TCP | UDP |
顺序 | 有序 | 无序 |
重传 | 不需要 | 需要(自实现) |
报文格式 | 流式 | 数据报(data-gram) |
拒绝服务防护 | 无 | 有 HelloVerifyRequest |
应用场景 | HTTPS | WebRTC / VoIP / SRTP / IoT |