端到端加密 (Beta)
在数据安全要求较高的场景下,除了使用媒体流加密功能,你还可以使用端到端加密功能,实现在发送端设备上对媒体流进行加密、在接收端设备上对媒体流进行解密,确保数据不会在传输过程中被第三方获取。
功能介绍
Web SDK 的端到端加密功能基于 WebRTC Encoded Transform 标准实现。由于 WebRTC Encoded Transform 还不够成熟、可能存在兼容或性能问题,目前端到端加密功能也存在一些使用限制。
浏览器兼容性
支持端到端加密的浏览器如下表,未列出的浏览器暂不支持:
浏览器 | 版本要求 |
---|---|
桌面端、移动端 Chrome | 87 或以上 |
桌面端、移动端 Safari | 15.4 或以上 |
注意事项
目前 Web SDK 的端到端加密功能有以下使用限制:
- 仅支持加密 VP8 格式的视频和 Opus 格式的音频。
- 仅支持在 Web 端之间接收和发布的音视频流,不能与其它平台的客户端互通,也不支持旁路推流服务。
- 由于浏览器基础组件冲突,与 SEI 信息发送功能不能同时开启。
实现原理
为了帮助理解实现原理,你需要了解一些相关概念:
- WebRTC Encoded Transform:在 WebRTC 中实现编码转换的一种技术。它提供了对 WebRTC 已编码数据的读写接口,允许开发者在编码后的数据上执行各种操作,例如转换编码格式、调整比特率或添加滤镜。
- WebRTC Encoded Transform 处理管线:用于处理 WebRTC 编码转换数据的一系列步骤或操作。
RTCRtpSender
和RTCRtpReceiver
:WebRTC 标准组件,RTCRtpSender
负责发送编码后的数据,RTCRtpReceiver
负责接收编码后的数据。二者用于建立 WebRTC Encoded Transform 处理管线。RTCRtpTransceiver
:用于管理音视频发送和接收的 WebRTC API。通过RTCRtpTransceiver
对象的sender
和receiver
属性,可以分别获取RTCRtpSender
和RTCRtpReceiver
对象。
对于每个音频或视频轨道,Web SDK 提供 getRTCRtpTransceiver
方法,用于获取当前轨道的 RTCRtpTransceiver
对象。通过 RTCRtpTransceiver
对象获取到 RTCRtpSender
和 RTCRtpReceiver
后,你可以建立 WebRTC Encoded Transform 处理管线,对音频或视频数据进行加密、解密转换。
前提条件
开始之前,确保你满足以下条件:
实现步骤
本节以 Chrome 浏览器为例,介绍如何实现视频的发送端加密和接收端解密。Safari 浏览器的实现步骤稍有不同。
示例中使用了 Web Crypto API 提供的 AES 加密算法。如果你需要使用特殊的加解密算法,可以使用 WebAssembly 实现自定义加解密算法。
发送端
发送端建立 WebRTC Encoded Transform 处理管线的步骤如下:
- 发布本地视频轨道后,调用 SDK 提供的
getRTCRtpTransceiver
方法获取本地视频轨道的RTCRtpTransceiver
实例,并通过sender
属性获取RTCRtpSender
实例。 - 调用浏览器原生的 Streams API 对视频流进行加密:
示例代码如下:
async function publish() {
// 发布本地视频轨道
await client.publish([localVideoTrack]);
// 获取本地视频轨道的 RTCRtpTransceiver 实例
const transceiver = localVideoTrack.getRTCRtpTransceiver();
if (!transceiver || !transceiver.sender) {
return;
}
// 获取 RTCRtpSender,用于控制发送的媒体数据
const sender = transceiver.sender;
// 定义在 Chrome 浏览器上的加密方式
if (isChrome) {
// 创建编码流,用于加密转换
const streams = sender.createEncodedStreams();
// 创建加密转换器
const transformer = new TransformStream({
transform(chunk, controller) {
// VP8 视频关键帧的前 7 个字节和非关键帧的前 3 个字节需要保留、不作加密,否则媒体传输会失败
const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
// encrypt 方法需开发者依据加密需求实现
// 示例中使用了 Web Crypto API 提供的 AES 加密算法
const encryptedChunk = encrypt(chunk, key, vp8ReservedSize);
controller.enqueue(encryptedChunk);
}
});
// 连接编码流和加密转换器,并将结果写入数据流
streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
}
}
接收端
接收端建立 WebRTC Encoded Transform 处理管线的步骤如下:
- 订阅远端视频流时,调用 SDK 提供的
getRTCRtpTransceiver
方法,获取远端视频轨道的RTCRtpTransceiver
实例。 - 调用浏览器原生的 Streams API 对视频流进行解密。
async function subscribe(user, mediaType) {
// 获取远端视频轨道的 RTCRtpTransceiver 实例
const transceiver = user.videoTrack.getRTCRtpTransceiver();
if (!transceiver || !transceiver.receiver) {
return;
}
// 获取 RTCRtpReceiver,用于控制接收的媒体数据
const receiver = transceiver.receiver;
// 定义在 Chrome 浏览器上的解密方式
if (isChrome) {
// 创建编码流,用于解密转换
const streams = receiver.createEncodedStreams();
// 创建解密转换器
const transformer = new TransformStream({
transform(chunk, controller) {
// VP8 视频关键帧的前 7 个字节和非关键帧的前 3 个字节不需要解密
const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
// decrypt 方法需开发者依据解密需求实现
const decryptedChunk = decrypt(chunk, key, vp8ReservedSize);
controller.enqueue (decryptedChunk);
}
});
// 连接编码流和解密转换器,并将结果写入数据流
streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
}
}
参考信息
开发注意事项
-
如果 SDK 发生断线重连,当前轨道对应的
RTCRtpTransceiver
实例可能发生变化。你可以通过以下回调来获取新的RTCRtpTransceiver
对象:- 本地轨道:transceiver-updated
- 远端轨道:transceiver-updated
-
开通端到端加密功能后,由于浏览器限制,通过 Web SDK 创建的本地轨道即使不需要加密,也需要创建空的加密管线,否则无法发送该轨道的媒体数据。参考以下代码创建空的加密管线:
JavaScriptasync function publish() {
await client.publish([localVideoTrack]);
const transceiver = localVideoTrack.getRTCRtpTransceiver();
if (!transceiver || !transceiver.sender) {
return;
}
const sender = transceiver.sender;
if (isChrome) {
const streams = sender.createEncodedStreams();
const transformer = new TransformStream({
transform(chunk, controller) {
// 直接写回
controller.enqueue(chunk);
}
});
streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
}
}
API 参考
端到端加密功能的相关 API 如下:
- Web SDK 提供的 API:
- 浏览器原生的 Streams API :