频道加密
媒体加密(AES-128/256)与传输加密的区别、配置方式、密钥轮换与支持情况。
「加密」在实时音视频里有两个完全不同的层次,混在一起谈最容易出错。这一页先把它们分开,再说明我们在每一层做到了什么。
两个层次
| 层次 | 保护什么 | 谁负责 | 我们的状态 |
|---|---|---|---|
| 传输加密 | 客户端 ↔ 媒体服务器之间的链路 | 平台(DTLS-SRTP + HTTPS/WSS) | ✅ 默认开启,无需配置 |
| 媒体加密(E2EE) | 端到端,媒体服务器也解不开 | 应用层密钥协商 + SDK 加密 | ➖ 未实现,显式 NOT_SUPPORTED |
传输加密(默认)
- 信令走
https:///wss://;媒体走 SRTP(LiveKit 默认 DTLS-SRTP)。 - 你不需要调任何 API;反向代理必须允许 WebSocket 升级,否则会退化成连不上(见 排障手册)。
AgoraRTC.on('security-policy-violation')会在浏览器安全策略(如混合内容、权限策略)拦截时触发,便于定位。
媒体加密:接口定义里有,运行时明确拒绝
声网 Web SDK 的形状里有一个 setEncryptionConfig:
client.setEncryptionConfig('aes-256-gcm2', secret, salt, true);我们的实现(控制面组件)对它做了显式分流:
// 允许显式声明「不加密」,便于把声网代码原样搬过来(secret 不能为空串,否则先报 INVALID_PARAMS)
client.setEncryptionConfig('none', 'no-secret-used');
// 除 'none' 之外的任何加密模式都会抛错,而不是静默成功
try {
client.setEncryptionConfig('aes-256-gcm2', 'a-32-byte-secret-material........', undefined, true);
} catch (error) {
// error.code === 'NOT_SUPPORTED'
// error.message === 'end-to-end encryption is not supported by the ni-meet media adapter yet'
}行为细节(来自实现,不是推测):
| 输入 | 行为 |
|---|---|
encryptionMode 为空 / secret 非字符串或空 | 抛 INVALID_PARAMS |
encryptionMode === 'none' | 接受,记录配置,不做加密 |
其它任何模式(aes-128-xts、aes-256-gcm2、sm4-128-ecb…) | 抛 NOT_SUPPORTED(AgoraRTCError.code === 'NOT_SUPPORTED') |
接口定义里的 EncryptionMode 类型是完整的(9 个取值),因为公开面必须与声网完全对齐;支持与否是运行时能力,不是类型。
需要 E2EE 时的可行路径
- 应用层加密(推荐给大多数团队):媒体仍是明文 SRTP,但对敏感内容(如白板数据、文件)在应用层做端到端加密,走数据通道。
- 自建 E2EE 媒体层:在
media/适配层注入支持 SFrame/Insertable Streams 的引擎,再按 公开 SDK 接口定义把setEncryptionConfig接到它上面。这属于自研投入,不是配置项。 - 换方案:如果合规硬性要求媒体级 E2EE 且不接受自研,这个需求与本项目的当前能力不匹配,应当尽早暴露而不是绕过。
密钥管理(无论哪条路径都适用)
- 密钥不进客户端包、不进前端环境变量;
- 每次会话独立密钥,会话结束即弃用;
- 密钥轮换不影响已签发 Token(Token 管授权,密钥管加密,两者独立);
- 审计日志只记「是否启用」与「轮换时间」,不记密钥本身。
常见误解
| 误解 | 事实 |
|---|---|
「Token 里有 salt 所以是加密的」 | salt 只是 Token 签名随机数,与媒体加密无关 |
「setEncryptionConfig 返回 void 就是成功了」 | 我们会在不支持的模式下抛错;不抛错只代表 'none' 或参数校验通过 |
| 「HTTPS 就是端到端加密」 | HTTPS 只到服务器;服务器之后是明文或另一段 TLS |
| 「开了加密会更卡」 | E2EE 有开销,但我们目前不提供,不存在这个权衡 |