20、安全机制:DTLS加密、SRTP协议、证书管理

说到WebRTC的安全机制,我其实挺感慨的。早些年做即时通讯的时候,大家总觉得加密是“锦上添花”的东西。直到有一次,我在一个公开WiFi环境下调试自己的Demo,抓包一看——音频流赤裸裸地暴露在网络上,谁都能听。嗯,从那以后,我再也不敢轻视安全这层了。

WebRTC把安全写进了规范里,不是可选项,是强制项。说白了,它要求所有媒体流必须加密传输。今天我们就来聊聊这背后的三驾马车:DTLS、SRTP,还有证书管理。

20.1 为什么需要DTLS?

先问一个问题:WebRTC的信令走的是HTTP或WebSocket,但媒体流走的是UDP。UDP本身没有加密能力,那怎么保证媒体数据不被窃听?

答案就是DTLS——Datagram Transport Layer Security。你可以把它理解成“跑在UDP上的TLS”。

我刚开始接触时也觉得奇怪:为什么不用TLS?因为TLS依赖TCP的可靠传输,而WebRTC的媒体流是基于UDP的,延迟更低,但可能丢包。DTLS专门为这种场景设计,它能在不可靠的传输层上建立安全通道。

核心要点: DTLS负责在WebRTC的媒体通道上协商加密密钥,并为后续的SRTP提供密钥材料。

20.2 DTLS握手流程

DTLS握手和TLS握手很像,但有几个关键区别。我画了一张图,帮你理清流程:

客户端 (Offerer) 服务端 (Answerer) ① ClientHello (cookie请求) ② HelloVerifyRequest (携带cookie) ③ ClientHello (带cookie) ④ ServerHello + 证书 + 密钥交换 ⑤ 客户端证书 + 密钥交换 + 完成 ⑥ 服务端完成 ✅ DTLS 握手完成,密钥就绪 → 使用协商出的密钥初始化 SRTP → 媒体流开始加密传输

注意看第二步的 HelloVerifyRequest。这是DTLS独有的机制,用来防止UDP层面的DDoS攻击。客户端必须先证明自己“愿意”通信,服务端才会继续握手。我在项目中遇到过有人试图用伪造IP打UDP洪水,DTLS这个cookie机制直接挡掉了。

20.3 SRTP:媒体流的加密保镖

DTLS握手完成后,双方有了共享密钥。但DTLS本身不适合加密大量媒体数据——开销太大。这时候SRTP上场了。

SRTP(Secure Real-time Transport Protocol)是RTP的加密版本。它用DTLS协商出的密钥,对音频和视频包进行加密。而且它支持 无状态加密,每个包独立加解密,丢包不影响后续包。

我的经验: 调试SRTP时最容易踩的坑是密钥派生参数不匹配。我曾经花了一整天排查为什么视频只有黑屏,最后发现是双方的SRTP profile没对齐。建议在SDP协商阶段打印出crypto参数,一目了然。

SRTP的加密流程大致如下:

  1. 密钥派生: 从DTLS握手得到的master key,派生出加密密钥、认证密钥、盐值。
  2. 包加密: 使用AES-CTR或AES-GCM对RTP payload加密。
  3. 消息认证: 添加HMAC标签,防止篡改。
  4. 序号保护: 使用ROC(Rollover Counter)防止重放攻击。

你想想看,一个音频包从麦克风采集到发送出去,经过编码、打包、加密、加认证,最后才扔到网络上。这中间任何一步出问题,对方听到的就是噪音。所以SRTP的稳定性非常关键。

20.4 证书管理:谁来证明你是谁?

DTLS握手需要证书。但WebRTC场景下,我们通常不使用CA签发的公网证书。为什么?因为WebRTC是点对点通信,两端都是“临时”的,没必要花几十块钱买个证书。

WebRTC的做法是:自签名证书。每个PeerConnection在建立时,内部会自动生成一个自签名的X.509证书。这个证书只用于本次会话,用完即弃。

关键点: 自签名证书虽然不被CA信任,但DTLS握手时双方会交换证书指纹(fingerprint)。指纹是一个哈希值,通过信令通道传递。只要指纹匹配,就证明对方持有对应的私钥。

具体流程是这样的:

  • Alice生成自签名证书,计算其SHA-256指纹。
  • Alice通过信令把指纹发给Bob。
  • Bob收到Alice的DTLS握手消息,拿到证书,计算指纹。
  • Bob对比两个指纹,一致则握手继续。

我曾经在调试一个跨平台项目时,发现Android和iOS之间始终连不上。查了半天,原来是Android端生成的证书指纹是SHA-1,而iOS端只接受SHA-256。嗯,后来统一改成SHA-256就解决了。

20.5 证书指纹的传递

指纹通常放在SDP的 a=fingerprint 属性中。举个例子:

a=fingerprint:sha-256 4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB:9B:6B:1B:3A:64:BD:B1:6C:3A:2C:0C:8C

注意,指纹必须通过信令通道安全传递。如果信令被篡改,攻击者可以替换指纹,从而实施中间人攻击。所以信令通道本身也需要加密——通常用HTTPS或WSS。

警告: 永远不要在未加密的信令通道上传递指纹。我在早期的一个Demo中犯过这个错,结果被同事抓包演示了MITM攻击,场面一度非常尴尬。

20.6 证书的生命周期

每个RTCPeerConnection对象都有自己的证书。但你可以选择复用证书:

// 生成一个证书,供多个连接复用
RTCPeerConnection.generateCertificate({
  name: 'ECDSA',
  namedCurve: 'P-256'
}).then(function(cert) {
  const config = {
    iceServers: [...],
    certificates: [cert]
  };
  const pc = new RTCPeerConnection(config);
});

复用证书的好处是减少握手开销。但要注意,证书有有效期,默认是30天。如果连接持续超过30天,需要重新协商。

我个人习惯在移动端使用ECDSA证书,因为它比RSA证书小,握手更快。移动端CPU资源有限,能省一点是一点。

20.7 常见问题与避坑

最后分享几个我踩过的坑:

  • 证书指纹不匹配: 检查两端是否使用相同的哈希算法。SHA-256是标准,别用SHA-1。
  • DTLS握手超时: UDP丢包严重时,DTLS握手可能失败。建议在ICE连接稳定后再触发DTLS。
  • SRTP解密失败: 确认密钥派生参数一致。特别是SSRC和ROC的同步。
  • 证书过期: 长时间运行的连接要处理证书过期事件,触发重新协商。
调试小技巧: 在Chrome的 chrome://webrtc-internals 中,可以看到DTLS和SRTP的状态。如果看到 DTLS: Failed,先检查证书指纹。

安全机制是WebRTC的基石。DTLS负责握手和密钥协商,SRTP负责媒体加密,证书管理确保身份可信。三者配合,才能保证你的音视频通话不被窃听、不被篡改。

下次调试时如果遇到加密相关的问题,记得先检查这三个环节。嗯,安全无小事,尤其是做实时通信。


公众号:蓝海资料掘金营,微信deep3321