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层面,通过RTCPeerConnection的setConfiguration来设置:
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两条路径分开,方便对比:
从图上可以清楚看到:SDES和DTLS-SRTP最终都通向SRTP加密,但密钥的获取方式完全不同。SDES是"明文传密钥",DTLS-SRTP是"握手生密钥"。
六、实战中的避坑指南
最后分享几个我实际项目中遇到的坑:
- 指纹不匹配:有一次排查了半天,发现是服务端证书更新了,但SDP里的指纹还是旧的。记住,每次DTLS证书变化,SDP里的a=fingerprint必须同步更新。
- SDES的密钥长度:有些老设备只支持40位的密钥,但现代WebRTC要求至少128位。对接时一定要确认双方支持的加密套件。
- 同时存在a=crypto和a=fingerprint:SDP里可以同时出现这两行吗?可以。但浏览器会优先使用DTLS-SRTP。如果DTLS握手失败,才会回退到SDES。不过我不建议这么搞,容易出兼容性问题。
- 自签名证书:DTLS-SRTP允许使用自签名证书,因为指纹验证本身就是一种信任机制。但要注意,有些网关会校验证书链,自签名证书可能被拒绝。
一个小技巧:调试时可以用chrome://webrtc-internals查看SDP里的a=crypto和a=fingerprint行。如果发现没有这两行,说明加密没协商成功,媒体流可能发不出去。
好了,关于媒体协商中的加密,核心就是这些。记住一句话:SDES是过去,DTLS-SRTP是现在和未来。选对策略,你的WebRTC应用才能既安全又稳定。
公众号:蓝海资料掘金营,微信deep3321