26、媒体协商中的Crypto:a=crypto行(SDES)、DTLS指纹(a=fingerprint)、安全策略选择

聊到WebRTC的媒体协商,很多人第一反应是SDP里的m=行、a=rtpmap这些。但说实话,真正让WebRTC区别于普通RTP传输的,是它内置的那套安全机制。今天我们就来掰扯清楚,媒体协商里跟加密相关的三个关键东西:a=crypto行(SDES)DTLS指纹(a=fingerprint),以及安全策略怎么选

核心观点:WebRTC强制要求加密,但加密的方式有两种——老派的SDES和现代主流的DTLS-SRTP。你选哪个,直接决定了SDP里写什么。

一、先搞清楚:WebRTC为什么要加密?

你想想看,音视频数据在网络上裸奔,多危险。随便一个抓包工具就能把你的通话内容扒个精光。所以WebRTC从设计之初就规定:媒体流必须加密

但加密不是一句话的事。你得协商好用什么密钥、怎么交换密钥、怎么验证对方身份。这些信息,全写在SDP里。

我个人习惯把媒体协商中的加密分成两个层面:

  • 密钥交换层面:怎么让双方拿到同一个加密密钥
  • 身份验证层面:怎么确认对方就是我要通话的那个人

这两个层面,分别对应了a=crypto和a=fingerprint这两行SDP属性。

二、a=crypto行:SDES的老路子

SDES,全称是SDP Security Descriptions。说白了,就是直接在SDP里把加密密钥传过去。

它的SDP长这样:

a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:UkVDSU5JVElORyBQUk9DRVNTIE9OIFZJREVPIENPTVA=

我来拆解一下:

  • 1:标签号,用于标识这条crypto行
  • AES_CM_128_HMAC_SHA1_80:加密套件,表示用AES-128-CTR模式加密,用HMAC-SHA1做完整性校验,80位的认证标签
  • inline::后面跟着的是base64编码的密钥材料

嗯,这里要注意——密钥直接写在SDP里。这意味着什么?如果SDP是通过不安全的信道传输的,那密钥就暴露了。所以SDES要求SDP必须通过TLS等安全信道传输。

我曾经踩过的坑:有一次在内部测试环境,我图省事直接用HTTP传SDP,结果SDES密钥被中间人截获,通话内容全被解密了。从那以后,我只要用SDES,一定确保SDP走HTTPS或WSS。

SDES支持的加密套件主要有这些:

加密套件 加密算法 认证算法 密钥长度
AES_CM_128_HMAC_SHA1_80 AES-128-CTR HMAC-SHA1 128位
AES_CM_128_HMAC_SHA1_32 AES-128-CTR HMAC-SHA1 128位
F8_128_HMAC_SHA1_80 AES-128-F8 HMAC-SHA1 128位

实际项目中,AES_CM_128_HMAC_SHA1_80是最常用的。80位的认证标签比32位更安全,虽然多占了一点带宽,但值得。

三、a=fingerprint:DTLS-SRTP的现代方案

DTLS-SRTP是现在WebRTC的主流方案。它不直接在SDP里传密钥,而是通过DTLS握手来交换密钥。

SDP里只放一个指纹:

a=fingerprint:sha-256 9E:8F:6D:3A:5C:7B:21:4E:8A:D0:1F:6C:3B:9A:2E:7D:5F:1C:4B:8E:3A:6D:9F:2C:5E:7A:1D:4F:8B:3E:6C

这个指纹是DTLS证书的哈希值。双方在DTLS握手时交换证书,然后验证证书的哈希是否跟SDP里的指纹一致。

为什么这样更安全?因为:

  • 密钥不在SDP里传输,而是通过DTLS握手动态生成
  • 指纹只用于验证身份,不泄露密钥信息
  • 即使SDP被截获,攻击者也拿不到媒体密钥

我个人的建议:新项目一律用DTLS-SRTP。SDES虽然简单,但安全性依赖SDP传输信道,容易出问题。DTLS-SRTP是标准推荐,也是浏览器默认行为。

四、安全策略选择:怎么选?

WebRTC的安全策略,说白了就是决定用SDES还是DTLS-SRTP。在API层面,通过RTCPeerConnectionsetConfiguration来设置:

const config = {
  iceServers: [...],
  sdpSemantics: 'unified-plan',
  // 安全策略设置
  certificates: [],  // 可选,自定义证书
  // 强制使用DTLS
  bundlePolicy: 'max-bundle',
  rtcpMuxPolicy: 'require'
};

const pc = new RTCPeerConnection(config);

实际上,浏览器默认就使用DTLS-SRTP。你不需要显式设置什么。但如果你在做Native开发,或者跟老系统对接,可能需要手动选择。

我总结了一个选择矩阵:

场景 推荐策略 原因
浏览器之间通话 DTLS-SRTP 默认支持,无需额外配置
浏览器与Native SDK DTLS-SRTP 主流SDK都支持
与老旧设备对接 SDES 有些老设备只支持SDES
内部安全网络 两者皆可 如果SDP走TLS,SDES也够用

注意:Chrome从某个版本开始已经移除了对SDES的支持。如果你只做浏览器端开发,根本不用操心SDES。但如果你做网关、MCU、或者跟SIP互通,SDES还是绕不开的。

五、核心逻辑:一张图看懂

下面这张图展示了媒体协商中加密相关的完整流程。我画的时候特意把SDES和DTLS-SRTP两条路径分开,方便对比:

WebRTC媒体协商加密流程 创建Offer/Answer 选择安全策略 SDES:密钥写在SDP里 a=crypto:1 AES_CM_128... ⚠ 依赖SDP传输信道安全 DTLS-SRTP:动态密钥交换 a=fingerprint:sha-256 ... ✓ 密钥不暴露,更安全 SRTP加密媒体流

从图上可以清楚看到:SDES和DTLS-SRTP最终都通向SRTP加密,但密钥的获取方式完全不同。SDES是"明文传密钥",DTLS-SRTP是"握手生密钥"。

六、实战中的避坑指南

最后分享几个我实际项目中遇到的坑:

  1. 指纹不匹配:有一次排查了半天,发现是服务端证书更新了,但SDP里的指纹还是旧的。记住,每次DTLS证书变化,SDP里的a=fingerprint必须同步更新。
  2. SDES的密钥长度:有些老设备只支持40位的密钥,但现代WebRTC要求至少128位。对接时一定要确认双方支持的加密套件。
  3. 同时存在a=crypto和a=fingerprint:SDP里可以同时出现这两行吗?可以。但浏览器会优先使用DTLS-SRTP。如果DTLS握手失败,才会回退到SDES。不过我不建议这么搞,容易出兼容性问题。
  4. 自签名证书:DTLS-SRTP允许使用自签名证书,因为指纹验证本身就是一种信任机制。但要注意,有些网关会校验证书链,自签名证书可能被拒绝。

一个小技巧:调试时可以用chrome://webrtc-internals查看SDP里的a=crypto和a=fingerprint行。如果发现没有这两行,说明加密没协商成功,媒体流可能发不出去。

好了,关于媒体协商中的加密,核心就是这些。记住一句话:SDES是过去,DTLS-SRTP是现在和未来。选对策略,你的WebRTC应用才能既安全又稳定。


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