实现端到端加密聊天的开发者指南:使用Virgil E3Kit和腾讯聊天SDK

10 分钟阅读
Feb 18, 2025

场景概述

此解决方案应由客户的应用程序基于Virgin Security的E3Kit和Chat SDK实现。E3Kit文档链接:Virgin Security E3Kit | Virgin Security

在阅读以下解决方案之前,请先了解GroupEncryption-End-to-End-Encryption-E3Kit | Virgin Security 文档,特别是JWT令牌(JSON Web Token)、创建频道/创建组、加密和解密消息。

使用前请在Virgin Security控制台中打开应用程序。此产品为付费产品。有关具体定价,请参见Pricing | Virgin Security

Virgin Security App Keys Interface
Application Specific Public-Private Key Pair
Create New App Key
App Key Details
Name: testim
Credentials: APP Key

下图显示了高层次的通信流程:

High-level communication flow diagram illustrating the interaction between users, their server, Tencent Server

适用产品

聊天

基本集成指南

1.聊天SDK初始化。

// 1. 从聊天控制台获取`SDKAppID`。
// 2. 初始化`config`对象。
V2TIMSDKConfig config = new V2TIMSDKConfig();
// 3. 指定日志输出级别。
config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO);
// 4. 添加`V2TIMSDKListener`事件监听器。`sdkListener`是`V2TIMSDKListener`的实现类。如果您不需要监听IM SDK事件,可以跳过此步骤。
V2TIMManager.getInstance().addIMSDKListener(sdkListener);
// 5. 初始化IM SDK。您可以在调用此API后立即调用登录API。
V2TIMManager.getInstance().initSDK(context, sdkAppID, config);

2.E3Kit初始化,传入控制台配置信息,并生成jwt。相应操作文档链接:GenerateClientTokens-GetStarted-E3Kit | VirgilSecurity

服务器生成jwttoken并将其发送给客户端。

// 生成jwt
// 应用密钥(您在Virgil仪表板上获得此密钥)
String appKeyBase64 = "MC4CAQAwBQYDK2VwBCIEINlK4BhgsijAbNmUqU6us0ZU9MGi+HxdYCA6TdZeHjR4";
byte[] appKeyData = ConvertionUtils.base64ToBytes(appKeyBase64);

// 加密库导入密钥对
VirgilCrypto crypto = new VirgilCrypto();
VirgilKeyPair keyPair = crypto.importPrivateKey(appKeyData);

// 初始化访问令牌签署者,用于签署用户的JWT
VirgilAccessTokenSigner accessTokenSigner = new VirgilAccessTokenSigner();

// 使用您在Virgil仪表板上获得的应用凭据:
String appId = "be00e10e4e1f4bf58f9b4dc85d79c77a";
String appKeyId = "70b447e321f3a0fd";
TimeSpan ttl = TimeSpan.fromTime(1, TimeUnit.HOURS); // 1小时 - JWT的生命周期

// 设置带有必需参数的JWT生成器:
JwtGenerator jwtGenerator =
    new JwtGenerator(appId, keyPair.getPrivateKey(), appKeyId, ttl, accessTokenSigner);

// 为用户生成JWT
// 请记住,您必须为每个用户提供唯一的JWT。
// 每个JWT包含唯一用户的身份(在本例中为 - Alice)。
// 身份可以是任何值:姓名、电子邮件、某些ID等。
String identity = "Alice";
Jwt aliceJwt = jwtGenerator.generateToken(identity);

// 作为结果,您将获得用户的JWT,它看起来像这样:
// "eyJraWQiOiI3MGI0NDdlMzIxZjNhMGZkIiwidHlwIjoiSldUIiwiYWxnIjoiVkVEUzUxMiIsImN0eSI6InZpcmdpbC1qd3Q7dj0xIn0.eyJleHAiOjE1MTg2OTg5MTcsImlzcyI6InZpcmdpbC1iZTAwZTEwZTRlMWY0YmY1OGY5YjRkYzg1ZDc5Yzc3YSIsInN1YiI6ImlkZW50aXR5LUFsaWNlIiwiaWF0IjoxNTE4NjEyNTE3fQ.MFEwDQYJYIZIAWUDBAIDBQAEQP4Yo3yjmt8WWJ5mqs3Yrqc_VzG6nBtrW2KIjP-kxiIJL_7Wv0pqty7PDbDoGhkX8CJa6UOdyn3rBWRvMK7p7Ak"。
// 您可以在注册或授权步骤中向用户提供JWT。
// 将JWT发送到客户端。
String jwtString = aliceJwt.stringRepresentation();

Android客户端初始化E3Kit,tokenCallback是上述服务器返回的jwttoken,User1是用户ID。

// 初始化E3Kit
// 创建包含身份、tokenCallback和上下文等必要参数的EThreeParams
EThreeParams params = new EThreeParams("User1",
   tokenCallback,context);
// 使用EThreeParams初始化E3Kit
EThree ethree = new EThree(params);

场景特定实现

用户登录

1.调用Chat SDK登录方法进行账户登录

String userID = "您的用户ID";
// 生成UserSig
String userSig = "来自您服务器的userSig";
V2TIMManager.getInstance().login(userID, userSig, new V2TIMCallback() {
   @Override
   public void onSuccess() {
       Log.i("imsdk", "成功");
   }
    @Override
   public void onError(int code, String desc) {
       // 以下错误代码表示`userSig`已过期,需要为登录再次生成新的userSig。
       // 1. ERR_USER_SIG_EXPIRED (6206)
       // 2. ERR_SVR_ACCOUNT_USERSIG_EXPIRED (70001)
       // 注意:在发生其他错误代码时,请勿调用登录API;否则,IM SDK可能会进入无限循环的登录状态。
       Log.i("imsdk", "失败,代码:" + code + ", 描述:" + desc);
   }
});

2.调用eThree.register方法将用户注册到virgilSecurity。相应操作文档链接: UserAuthentication-E3Kit | VirgilSecurity。

注意:im_ID的用户和在virgilsecurity上注册的用户_id应保持一致

开始一对一聊天

1.调用ethree.createRatchetChannel方法在E3Kit中创建一对一会话(User1和User2),相应操作文档链接:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/double-ratchet/#create-channel

User1与User2创建频道

// 创建一对一频道
ethree.createRatchetChannel(users.get("User2"))
      .addCallback(new OnResultListener<RatchetChannel>() {
          @Override public void onSuccess(RatchetChannel ratchetChannel) {
              // 渠道创建成功并保存在本地!
          }

          @Override public void onError(@NotNull Throwable throwable) {
              // 错误处理
          }
      });

User2可以加入频道

// 加入频道
ethree.joinRatchetChannel(users.get("User1"))
      .addCallback(new OnResultListener<RatchetChannel>() {
    @Override public void onSuccess(RatchetChannel ratchetChannel) {
        // 渠道已加入并保存在本地!
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // 错误处理
    }
});

一对一聊天消息加密和解密

1.使用上述创建的频道来加密消息和链接文档:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/double-ratchet/?#encrypt-and-decrypt-messages

// 一对一聊天消息加密
// 准备一条消息
String messageToEncrypt = "你好,User2!";

String encrypted = channel.encrypt(messageToEncrypt);

2.将加密的消息内容发送到Chat SDK,并以自定义消息发送

// `msgID`由API返回以供按需使用
String msgID = V2TIMManager.getInstance().sendC2CCustomMessage("virgil加密消息", "接收者用户ID", new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
    // 一对一文本消息发送成功
}
    @Override
public void onError(int code, String desc) {
    // 发送一对一文本消息失败
}
});

3.对方接收自定义消息后

// 设置事件监听器
V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);

/**
* 接收自定义一对一消息
* @param msgID 消息ID
* @param sender 发送者信息
* @param customData 发送的内容
*/
public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) {
Log.i("onRecvC2CCustomMessage", "msgID:" + msgID + ", 来自:" + sender.getNickName() + ", 内容:" + new String(customData));
// 调用E3Kit解密消息
}

4.对方调用E3Kit进行解密并渲染,如下所示:

// 解密消息
String decrypted = channel.decrypt(encrypted);

启动频道(群组)

1.调用ethree.createGroup创建群组,文档链接:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#create-group-chat

// 创建群组
ethree.createGroup(groupId, users).addCallback(new OnResultListener<Group>() {
    @Override public void onSuccess(Group group) {
        // 群组创建成功并保存在本地!
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // 错误处理
    }
});

2.如果需要添加群组成员,下面的代码如下。您可以调用remove方法删除群组成员。文档链接:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#add-new-participant

// 添加群组成员
group.add(users.get("Den")).addCallback(new OnCompleteListener() {
    @Override public void onSuccess() {
        // Den被添加!
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // 错误处理
    }
});

3.调用Chat SDK的createGroup创建群组,joinGroup或inviteUserToGroup添加群组成员。

V2TIMManager.getInstance().createGroup(V2TIMManager.GROUP_TYPE_WORK, null, "groupA", new V2TIMValueCallback<String>() {
 @Override
 public void onSuccess(String s) {
     // 群组创建成功
 }
  @Override
 public void onError(int code, String desc) {
     // 创建群组失败
 }
});
// 监听群组创建通知
V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
 @Override
 public void onGroupCreated(String groupID) {
     // 群组已创建。`groupID`是创建的群组的ID。
 }
});
// 邀请`userA`用户加入`groupA`群组
List<String> userIDList = new ArrayList<>();
userIDList.add("userA");
V2TIMManager.getGroupManager().inviteUserToGroup("groupA", userIDList, new V2TIMValueCallback<List<V2TIMGroupMemberOperationResult>>() {
 @Override
 public void onSuccess(List<V2TIMGroupMemberOperationResult> v2TIMGroupMemberOperationResults) {
     // 用户成功被邀请加入群组
 }
  @Override
 public void onError(int code, String desc) {
     // 邀请用户加入群组失败
 }
});
// 监听群组邀请事件
V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
 @Override
 public void onMemberInvited(String groupID, V2TIMGroupMemberInfo opUser, List<V2TIMGroupMemberInfo> memberList) {
     // 用户被邀请加入群组。此回调可以包含一些UI提示。
 }
});

群聊消息加密和解密

1.使用上述创建的群组来加密消息并链接文档:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#encrypt-and-decrypt-messages

// 群组消息加密
// 准备一条消息
String messageToEncrypt = "你好,Bob和Carol!";

String encrypted = group.encrypt(messageToEncrypt);

2.将加密的消息内容发送到腾讯Chat SDK,并以自定义消息发送

String msgID = V2TIMManager.getInstance().sendGroupCustomMessage("virgil加密消息".getBytes(), "groupID", V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
    // 自定义群组消息发送成功
}

3.当对方腾讯云Chat SDK接收到自定义消息后,

// 设置事件监听器
V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);

/**
* 接收自定义群组消息
* @param msgID 消息ID
* @param groupID 群组ID
* @param sender 发送者的群组成员信息
* @param customData 发送的内容
*/
public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {
Log.i("onRecvGroupCustomMessage", "msgID:" + msgID + ", groupID:" + groupID + ", 来自:" + sender.getNickName() + ", 内容:" + new String(customData));
// 调用E3Kit解密消息
}

4.对方进行解密并渲染,如下所示:

// 群组消息解密
String decrypted = group.decrypt(encrypted, users.get("Alice"));

注意:在使用此端到端加密方案后,imsdk的本地聊天记录搜索功能将无法使用。

立即订购

点击这里快速进入购买页面进行订单。