在实时音视频互动过程中,你需要对媒体流加密,从而保障用户的数据安全。声网提供内置加密和自定义加密方案,你可以根据需要选择合适的加密方案。
下图描述了启用媒体流加密后的数据传输流程:
声网在 GitHub 上提供以下实现了媒体流加密功能的开源示例项目:
你可以下载体验并参考源代码。
在启用媒体流加密前,请确保已参考《快速开始》文档在你的项目中实现基本的音视频通话或直播功能。
1. 获取加密库(仅 iOS)
自 3.2.0 版本起,声网iOS SDK 中的 AgoraRtcCryptoLoader.framework
加密库合并入 AgoraRtcKit.framework
。如果你使用 3.2.0 及以上版本的 SDK,获取 AgoraRtcKit.framework
后即可使用内置的加密方案。
如果你使用 3.2.0 以下版本的 SDK,除获取 AgoraRtcKit.framework
外,还需参考以下步骤获取 AgoraRtcCryptoLoader.framework
加密库并导入类:
选择以下一种方法集成加密库:
a. 开始前确保你已安装 Cocoapods。参考 Getting Started with CocoaPods 安装说明。
b. 在 Terminal 里进入项目根目录,并运行 pod init
命令。项目文件夹下会生成一个 Podfile
文本文件。
c. 打开 Podfile
文件,修改文件为如下内容。注意将 Your App
替换为你的 Target 名称,并将 version
替换为你需集成的 SDK 版本。
# platform :ios, '9.0'
target 'Your App' do
pod 'AgoraRtcEngine_iOS_Crypto', 'version'
end
d. 在 Terminal 内运行 pod install
命令安装声网 SDK。成功安装后,Terminal 中会显示 Pod installation complete!
,此时项目文件夹下会生成一个 xcworkspace
文件。
e. 打开新生成的 xcworkspace
文件。
a. 将 SDK 包中的 AgoraRtcCryptoLoader.framework
复制到项目文件夹下。
b. 打开 Xcode(以 Xcode 11.0 为例),进入 TARGETS > Project Name > General > Frameworks, Libraries, and Embedded Content 菜单,点击 +,再点击 Add Other… 添加 AgoraRtcCryptoLoader.framework
,并确保添加的动态库 Embed 属性设置为 Embed & Sign。
在项目中导入 AgoraRtcCryptoLoader
类:
// Swift
import AgoraRtcCryptoLoader
// Objective-C
#import <AgoraRtcCryptoLoader/AgoraRtcCryptoLoader.h>
2. 生成密钥
在你的服务端,参考以下命令通过 OpenSSL 随机生成 String 型、32 字节的密钥。
// 随机生成一个 string 型、32 字节的密钥,并将该密钥传入 enableEncryption 的 encryptionKey 参数。
openssl rand -hex 32
dba643c8ba6b6dc738df43d9fd624293b4b12d87a60f518253bd10ba98c48453
客户端从服务端获取 String 型密钥,并在调用 enableEncryption
时传入 SDK。
3. 生成盐
在你的服务端,参考以下命令通过 OpenSSL 随机生成 Base64 编码、32 字节的盐。你也可以参考声网在 GitHub 上提供的 C++ 示例代码,在服务端随机生成 byte array 型的盐,然后转换成 Base64 编码。
// 随机生成一个 Base64 编码、32 字节的盐,并将该盐传入 enableEncryption 的 encryptionKdfSalt 参数。
openssl rand -base64 32
X5w9T+50kzxVOnkJKiY/lUk82/bES2kATOt3vBuGEDw=
客户端从服务端获取 Base64 编码的盐。
客户端将盐值从 Base64 编码解码为长度为 32 的 NSData,然后在调用 enableEncryption
时传入 SDK。
4. 开启内置加密
在加入频道前,调用 enableEncryption
方法启用内置加密。
自 v3.4.5 起,声网推荐使用 AgoraEncryptionModeAES128GCM2
或 AgoraEncryptionModeAES256GCM2
加密模式,并设置密钥和盐。
GCM2
加密模式使用了安全性更高的密钥派生函数且支持盐。如果你选择其他加密模式,则只需设置加密模式和密钥。@interface Server
+ (NSString*) getEncryptionKdfSaltBase64;
+ (NSString*) getEncryptionSecret;
@end
@interface Example
- (BOOL) enableEncryption:(AgoraRtcEngineKit* _Nonnull)engine;
@end
@implementation Example
- (BOOL) enableEncryption:(AgoraRtcEngineKit*)agoraKit
{
if(!agoraKit) return NO;
NSString* encryptionKdfSaltBase64 = [Server getEncryptionKdfSaltBase64];
NSString* encryptionSecret = [Server getEncryptionSecret];
if(!encryptionKdfSaltBase64 || !encryptionSecret)
return NO;
NSData* encryptionKdfSalt = [[NSData alloc] initWithBase64EncodedString:encryptionKdfSaltBase64 options:0];
AgoraEncryptionConfig* config = [[AgoraEncryptionConfig alloc] init];
config.encryptionMode = AgoraEncryptionModeAES128GCM2;
config.encryptionKey = encryptionSecret;
config.encryptionKdfSalt = encryptionKdfSalt;
int result = [agoraKit enableEncryption:YES encryptionConfig:config];
return (0 == result);
}
@end
声网提供 C++ 的 registerPacketObserver
方法及 IPacketObserver
类,帮助你实现自定义加密功能。
.mm
文件中实现。.mm
文件的开头需要引入 C++ 头文件: #include <AgoraRtcKit/IAgoraRtcEngine.h>
。参考步骤如下:
在 .mm
文件中通过 IPacketObserver
类实现你自定义的数据加密算法。
class AgoraCustomEncryptionObserver : public agora::rtc::IPacketObserver
{
public:
EVP_CIPHER_CTX *ctx_audio_send;
EVP_CIPHER_CTX *ctx_audio_receive;
EVP_CIPHER_CTX *ctx_video_send;
EVP_CIPHER_CTX *ctx_video_receive;
AgoraCustomEncryptionObserver()
{
m_txAudioBuffer.resize(1024);
m_rxAudioBuffer.resize(1024);
m_txVideoBuffer.resize(1024);
m_rxVideoBuffer.resize(1024);
}
virtual bool onSendAudioPacket(Packet& packet)
{
int outlen;
// 加密数据包
unsigned char outbuf[1024];
EVP_EncryptInit_ex(ctx_audio_send, EVP_aes_256_gcm(), NULL, NULL, NULL);
EVP_CIPHER_CTX_ctrl(ctx_audio_send, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
EVP_EncryptInit_ex(ctx_audio_send, NULL, NULL, gcm_key, gcm_iv);
EVP_EncryptUpdate(ctx_audio_send, outbuf, &outlen, packet.buffer, packet.size);
// 将加密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = outbuf;
packet.size = outlen;
return true;
}
virtual bool onSendVideoPacket(Packet& packet)
{
int outlen;
// 加密数据包
unsigned char outbuf[1024];
EVP_EncryptInit_ex(ctx_video_send, EVP_aes_256_gcm(), NULL, NULL, NULL);
EVP_CIPHER_CTX_ctrl(ctx_video_send, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
EVP_EncryptInit_ex(ctx_video_send, NULL, NULL, gcm_key, gcm_iv);
EVP_EncryptUpdate(ctx_video_send, outbuf, &outlen, packet.buffer, packet.size);
// 将加密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = outbuf;
packet.size = outlen;
return true;
}
virtual bool onReceiveAudioPacket(Packet& packet)
{
int outlen;
// 加密数据包
unsigned char outbuf[1024];
EVP_DecryptInit_ex(ctx_audio_receive, EVP_aes_256_gcm(), NULL, NULL, NULL);
EVP_CIPHER_CTX_ctrl(ctx_audio_receive, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
EVP_DecryptInit_ex(ctx_audio_receive, NULL, NULL, gcm_key, gcm_iv);
EVP_DecryptUpdate(ctx_audio_receive, outbuf, &outlen, packet.buffer, packet.size);
// 将解密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = outbuf;
packet.size = outlen;
return true;
}
virtual bool onReceiveVideoPacket(Packet& packet)
{
int outlen;
// 加密数据包
unsigned char outbuf[1024];
EVP_DecryptInit_ex(ctx_video_receive, EVP_aes_256_gcm(), NULL, NULL, NULL);
EVP_CIPHER_CTX_ctrl(ctx_video_receive, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
EVP_DecryptInit_ex(ctx_video_receive, NULL, NULL, gcm_key, gcm_iv);
EVP_DecryptUpdate(ctx_video_receive, outbuf, &outlen, packet.buffer, packet.size);
// 将解密后数据的缓存地址和缓存大小发回 SDK
packet.buffer = outbuf;
packet.size = outlen;
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;
};
在加入频道前,注册数据包观测器,在语音或视频数据包传输时接收事件。
// Swift
AgoraCustomEncryption.registerPacketProcessing(agoraKit)
在 .mm
文件中调用 registerPacketObserver
实现注册数据包观测器。
+ (void)registerPacketProcessing:(AgoraRtcEngineKit *)rtcEngineKit {
if (!rtcEngineKit) {
return;
}
// 获取 Native SDK 的 C++ 句柄
agora::rtc::IRtcEngine* rtc_engine = (agora::rtc::IRtcEngine*)rtcEngineKit.getNativeHandle;
// 注册数据包观测器
rtc_engine->registerPacketObserver(&s_packetObserver);
s_packetObserver.ctx_audio_send = EVP_CIPHER_CTX_new();
s_packetObserver.ctx_video_send = EVP_CIPHER_CTX_new();
s_packetObserver.ctx_audio_receive = EVP_CIPHER_CTX_new();
s_packetObserver.ctx_video_receive = EVP_CIPHER_CTX_new();
}
离开频道后,取消注册数据包观测器。
// Swift
AgoraCustomEncryption.deregisterPacketProcessing(agoraKit)
在 .mm
文件中调用 registerPacketObserver(NULL)
实现取消注册数据包观测器。
+ (void)deregisterPacketProcessing:(AgoraRtcEngineKit *)rtcEngineKit {
if (!rtcEngineKit) {
return;
}
// 获取 Native SDK 的 C++ 句柄
agora::rtc::IRtcEngine* rtc_engine = (agora::rtc::IRtcEngine*)rtcEngineKit.getNativeHandle;
// 取消注册数据包观测器
rtc_engine->registerPacketObserver(NULL);
EVP_CIPHER_CTX_free(s_packetObserver.ctx_audio_send);
EVP_CIPHER_CTX_free(s_packetObserver.ctx_video_send);
EVP_CIPHER_CTX_free(s_packetObserver.ctx_audio_receive);
EVP_CIPHER_CTX_free(s_packetObserver.ctx_video_receive);
}