在实时音视频互动过程中,开发者需要对媒体流加密,从而保障用户的数据安全。Agora 提供内置加密方案和自定义加密方案,区别如下:
你可以根据需要选择合适的加密方案。
下图描述了启用媒体流加密后的数据传输流程:
在启用媒体流加密前,请确保已在你的项目中实现基本的实时音视频功能。详见实现音视频通话或实现互动直播。
在加入频道前,调用 enableEncryption
方法开启内置加密,并设置加密模式和密钥。
Agora 支持 4 种加密模式:
AES_128_XTS
: 128 位 AES 加密,XTS 模式。AES_128_ECB
: 128 位 AES 加密,ECB 模式。AES_256_XTS
: 256 位 AES 加密,XTS 模式。SM4_128_ECB
: 128 位国密 SM4 加密,ECB 模式。// 创建一个 EncryptionConfig 实例
EncryptionConfig config = new EncryptionConfig();
// 设置加密模式为国密 SM4 加密模式
config.encryptionMode = EncryptionConfig.SM4_128_ECB;
// 设置加密密钥
config.encryptionKey = "xxxxxxxxxxxxxxxx";
// 启用内置加密
mRtcEngine.enableEncryption(true, config);
Agora 在各平台上提供了 C++ 的 registerPacketObserver
方法及 IPacketObserver
类,帮助你实现自定义加密功能。参考步骤如下:
在加入频道前,调用 registerPacketObserver
注册数据包观测器,从而在语音或视频数据包传输时接收事件。
virtual int registerPacketObserver(IPacketObserver* observer);
实现一个 IPacketObserver
类:
class IPacketObserver
{
public:
struct Packet
{
// 需要发送或接收的数据的缓存地址
const unsigned char* buffer;
// 需要发送或接收的数据的缓存大小
unsigned int size;
};
// 已发送音频包回调
// 在音频包被发送给远端用户前触发
// @param packet 详见: Packet
// @return
// - true: 发送音频包
// - false: 丢弃音频包
virtual bool onSendAudioPacket(Packet& packet) = 0;
// 已发送视频包回调
// 在视频包被发送给远端用户前触发
// @param packet 详见: Packet
// @return
// - true: 发送视频包
// - false: 丢弃视频包
virtual bool onSendVideoPacket(Packet& packet) = 0;
// 收到音频包回调
// 在收到远端用户的音频包前触发
// @param packet 详见: Packet
// @return
// - true: 发送音频包
// - false: 丢弃音频包
virtual bool onReceiveAudioPacket(Packet& packet) = 0;
// 收到视频包回调
// 在收到远端用户的视频包前触发
// @param packet 详见: Packet
// @return
// - true: 发送视频包
// - false: 丢弃视频包
virtual bool onReceiveVideoPacket(Packet& packet) = 0;
};
继承 IPacketObserver
,并在你的 app 上使用你自定义的数据加密算法。
class AgoraPacketObserver : public agora::IPacketObserver
{
public:
AgoraPacketObserver()
{
m_txAudioBuffer.resize(2048);
m_rxAudioBuffer.resize(2048);
m_txVideoBuffer.resize(2048);
m_rxVideoBuffer.resize(2048);
}
virtual bool onSendAudioPacket(Packet& packet)
{
int i;
// 加密数据包
const unsigned char* p = packet.buffer;
const unsigned char* pe = packet.buffer+packet.size;
for (i = 0; p < pe && i < m_txAudioBuffer.size(); ++p, ++i)
{
m_txAudioBuffer[i] = *p ^ 0x55;
}
// 将加密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = &m_txAudioBuffer[0];
packet.size = i;
return true;
}
virtual bool onSendVideoPacket(Packet& packet)
{
int i;
// 加密数据包
const unsigned char* p = packet.buffer;
const unsigned char* pe = packet.buffer+packet.size;
for (i = 0; p < pe && i < m_txVideoBuffer.size(); ++p, ++i)
{
m_txVideoBuffer[i] = *p ^ 0x55;
}
// 将加密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = &m_txVideoBuffer[0];
packet.size = i;
return true;
}
virtual bool onReceiveAudioPacket(Packet& packet)
{
int i = 0;
// 解密数据包
const unsigned char* p = packet.buffer;
const unsigned char* pe = packet.buffer+packet.size;
for (i = 0; p < pe && i < m_rxAudioBuffer.size(); ++p, ++i)
{
m_rxAudioBuffer[i] = *p ^ 0x55;
}
// 将解密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = &m_rxAudioBuffer[0];
packet.size = i;
return true;
}
virtual bool onReceiveVideoPacket(Packet& packet)
{
int i = 0;
// 解密数据包
const unsigned char* p = packet.buffer;
const unsigned char* pe = packet.buffer+packet.size;
for (i = 0; p < pe && i < m_rxVideoBuffer.size(); ++p, ++i)
{
m_rxVideoBuffer[i] = *p ^ 0x55;
}
// 将解密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = &m_rxVideoBuffer[0];
packet.size = i;
return true;
}
private:
// 发送音频数据 buffer
std::vector<unsigned char> m_txAudioBuffer;
// 发送视频数据 buffer
std::vector<unsigned char> m_txVideoBuffer;
// 接收音频数据 buffer
std::vector<unsigned char> m_rxAudioBuffer;
// 接收视频数据 buffer
std::vector<unsigned char> m_rxVideoBuffer;
};
实现一个 java 包装程序。例如:
JNIEXPORT jint JNICALL Java_io_agora_video_demo_RtcEngineEncryption_enableEncryption(JNIEnv *env, jclass clazz, jlong engineHandle)
{
typedef jint (*PFN_registerAgoraPacketObserver)(void* engine, agora::IPacketObserver* observer);
void* handle = dlopen("libagora-rtc-sdk-jni.so", RTLD_LAZY);
if (!handle)
{
__android_log_print(ANDROID_LOG_ERROR, "agora encrypt demo",
"cannot find libagora-rtc-sdk-jni.so");
return -1;
}
PFN_registerAgoraPacketObserver pfn = (PFN_registerAgoraPacketObserver)dlsym(handle, "registerAgoraPacketObserver");
if (!pfn)
{
__android_log_print(ANDROID_LOG_ERROR, "aogra encrypt demo", "cannot find registerAgoraPacketObserver");
return -2;
}
return pfn((void*)engineHandle, &s_packetObserver);
}
Java wrapper:
public class RtcEngineEncryption {
static {
System.loadLibrary("agora-encrypt-demo-jni");
}
public static native int enableEncryption(long rtcEngineHandle);
}
调用步骤 4 中实现的 registerAgoraPacketObserver
为 IPacketObserver
类注册一个实例。