端到端加密聊天

端到端加密聊天

方案概述

该方案要由客户应用侧来基于 virgilsecurity的E3Kit + 腾讯云im sdk进行实现,E3Kit对应的传送门:Virgil E3Kit | Virgil Security
阅读下列方案前先对 Group Encryption - End-to-End Encryption - E3Kit | Virgil Security的文档进行了解,特别是jwt token(JSON Web Token), create channel/create group,Encrypt and decrypt messages部分。
使用前先在virgilsecurity控制台开通应用,该产品是收费产品,具体定价看Pricing | Virgil Security

img


整体系统交互流程如下:

img



方案详解(以安卓为例)

初始化

1. 腾讯云 im sdk初始化
// 1. 从即时通信 IM 控制台获取应用 SDKAppID。
// 2. 初始化 config 对象。
V2TIMSDKConfig config = new V2TIMSDKConfig();
// 3. 指定 log 输出级别。
config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO);
// 4. 添加 V2TIMSDKListener 的事件监听器,sdkListener 是 V2TIMSDKListener 的实现类,如果您不需要监听 IM SDK 的事件,这个步骤可以忽略。
V2TIMManager.getInstance().addIMSDKListener(sdkListener);
// 5. 初始化 IM SDK,调用这个接口后,可以立即调用登录接口。
V2TIMManager.getInstance().initSDK(context, sdkAppID, config);
2.E3Kit初始化,传入控制台的配置信息,生成jwt。对应操作文档链接:Generate Client Tokens - Get Started - E3Kit | Virgil Security
服务器生成jwt token,下发给客户端
// 生成jwt
// App Key (you got this Key at the Virgil Dashboard)
String appKeyBase64 = "MC4CAQAwBQYDK2VwBCIEINlK4BhgsijAbNmUqU6us0ZU9MGi+HxdYCA6TdZeHjR4";
byte[] appKeyData = ConvertionUtils.base64ToBytes(appKeyBase64);

// Crypto library imports a key pair
VirgilCrypto crypto = new VirgilCrypto();
VirgilKeyPair keyPair = crypto.importPrivateKey(appKeyData);

// Initialize an access token signer that signs users JWTs
VirgilAccessTokenSigner accessTokenSigner = new VirgilAccessTokenSigner();

// Use your App Credentials you got at the Virgil Dashboard:
String appId = "be00e10e4e1f4bf58f9b4dc85d79c77a";
String appKeyId = "70b447e321f3a0fd";
TimeSpan ttl = TimeSpan.fromTime(1, TimeUnit.HOURS); // 1 hour - JWT's lifetime

// Setup a JWT generator with the required parameters:
JwtGenerator jwtGenerator =
new JwtGenerator(appId, keyPair.getPrivateKey(), appKeyId, ttl, accessTokenSigner);

// Generate a JWT for a user
// Remember that you must provide each user with a unique JWT.
// Each JWT contains unique user's identity (in this case - Alice).
// Identity can be any value: name, email, some id etc.
String identity = "Alice";
Jwt aliceJwt = jwtGenerator.generateToken(identity);

// As a result you get user's JWT, it looks like this: "eyJraWQiOiI3MGI0NDdlMzIxZjNhMGZkIiwidHlwIjoiSldUIiwiYWxnIjoiVkVEUzUxMiIsImN0eSI6InZpcmdpbC1qd3Q7dj0xIn0.eyJleHAiOjE1MTg2OTg5MTcsImlzcyI6InZpcmdpbC1iZTAwZTEwZTRlMWY0YmY1OGY5YjRkYzg1ZDc5Yzc3YSIsInN1YiI6ImlkZW50aXR5LUFsaWNlIiwiaWF0IjoxNTE4NjEyNTE3fQ.MFEwDQYJYIZIAWUDBAIDBQAEQP4Yo3yjmt8WWJ5mqs3Yrqc_VzG6nBtrW2KIjP-kxiIJL_7Wv0pqty7PDbDoGhkX8CJa6UOdyn3rBWRvMK7p7Ak".
// You can provide users with JWT at registration or authorization steps.
// Send a JWT to client-side.
String jwtString = aliceJwt.stringRepresentation();
客户端安卓端初始化E3Kit, tokenCallback是上面服务器返回的jwt token, User1是用户id
// 初始化E3Kit
// create EThreeParams with mandatory parameters
// such as identity, tokenCallback and context
EThreeParams params = new EThreeParams("User1",
tokenCallback,
context);

// initialize E3Kit with the EThreeParams
EThree ethree = new EThree(params);

用户登陆

1. 调用腾讯云 im sdk login 接口进行账号登陆
String userID = "your user id";
//usersig生成看下面链接
//即时通信 IM 生成 UserSig-服务端 API-文档中心-腾讯云
String userSig = "userSig from your server";
V2TIMManager.getInstance().login(userID, userSig, new V2TIMCallback() {
@Override
public void onSuccess() {
Log.i("imsdk", "success");
}

@Override
public void onError(int code, String desc) {
// 如果返回以下错误码,表示使用 UserSig 已过期,请您使用新签发的 UserSig 进行再次登录。
// 1. ERR_USER_SIG_EXPIRED(6206)
// 2. ERR_SVR_ACCOUNT_USERSIG_EXPIRED(70001)
// 注意:其他的错误码,请不要在这里调用登录接口,避免 IM SDK 登录进入死循环。
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
2.调用**eThree.**register接口把用户注册到virgilsecurity上,对应操作文档链接:User Authentication - E3Kit | Virgil Security
注意:im的user_id和注册到virgilsecurity上的user_id要保持一致

发起单聊会话

1.在E3Kit中调用 ethree.createRatchetChannel 接口创建one-to-one会话(User1和User2), 对应操作文档链接:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/double-ratchet/?#create-channel
User1创建了跟User2之间的channel
// 创建one-to-one channel
ethree.createRatchetChannel(users.get("User2"))
.addCallback(new OnResultListener<RatchetChannel>() {
@Override public void onSuccess(RatchetChannel ratchetChannel) {
// Channel created and saved locally!
}

@Override public void onError(@NotNull Throwable throwable) {
// Error handling
}
});
然后User2就可以加入channel
// 单聊的另一方加入channel
ethree.joinRatchetChannel(users.get("User1"))
.addCallback(new OnResultListener<RatchetChannel>() {
@Override public void onSuccess(RatchetChannel ratchetChannel) {
// Channel joined and saved locally!
}

@Override public void onError(@NotNull Throwable throwable) {
// Error handling
}
});

单聊收发消息加解密

// 单聊消息加密
// prepare a message
String messageToEncrypt = "Hello, User2!";

String encrypted = channel.encrypt(messageToEncrypt);
1. 加密后的消息内容传给腾讯云 im sdk,使用自定义消息发送
// API 返回 msgID,按需使用
String msgID = V2TIMManager.getInstance().sendC2CCustomMessage("virgil加密消息".getBytes(), "receiver_userID", new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
// 发送单聊文本消息成功
}

@Override
public void onError(int code, String desc) {
// 发送单聊文本消息失败
}
});
2. 对端腾讯云im sdk收到自定义消息后
// 设置事件监听器
V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);

// 接收单聊自定义消息
/**
* 收到 C2C 自定义消息
*
* @param msgID 消息唯一标识
* @param sender 发送方信息
* @param text 发送内容
*/
public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] bEncrypted) {
// 可解析消息并调用vilgil的channel.decrypt方法去解密
}
4.调用E3Kit解密后渲染出来,如下
// 解密消息
String decrypted = channel.decrypt(encrypted);

发起群聊会话

// 建群
ethree.createGroup(groupId, users).addCallback(new OnResultListener<Group>() {
@Override public void onSuccess(Group group) {
// Group created and saved locally!
}

@Override public void onError(@NotNull Throwable throwable) {
// Error handling
}
});
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 was added!
}

@Override public void onError(@NotNull Throwable throwable) {
// Error handling
}
});
1. 调用腾讯云im sdk的createGroup来创建群组,joinGroupinviteUserToGroup进行群成员的新增
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 上的提示
}
});

群聊收发消息加解密

//群消息加密
// prepare a message
String messageToEncrypt = "Hello, Bob and Carol!";

String encrypted = group.encrypt(messageToEncrypt);
1. 加密后的消息内容传给腾讯云im sdk,使用自定义消息发送
String msgID = V2TIMManager.getInstance().sendGroupCustomMessage("virgil加密消息".getBytes(), "groupID", V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
// 发送群聊自定义消息成功
}

@Override
public void onError(int code, String desc) {
// 发送群聊自定义消息失败
}
});
3.对端腾讯云im sdk收到群自定义消息
/**
* 接收群聊自定义消息
* @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 + ", from:" + sender.getNickName() + ", content:" + new String(customData));
//可解析消息并调用vilgil的group.decrypt方法去解密
}
4.调用E3Kit解密后渲染出来,如下
//群消息解密
String decrypted = group.decrypt(encrypted, users.get("Alice"));
注意:使用该端到端加密方案后,im sdk的本地聊天记录搜索功能将不可用

即时通信 IM - 交流群

加入腾讯云即时通信 IM 技术交流群,您将获得:
● 可靠的技术支持

● 详细的产品信息

● 紧密的行业交流
Telegram交流群:点击加入
WhatsApp交流群:点击加入