Implementing End-to-End Encrypted Chat with Virgil E3Kit and Tencent Chat SDK: A Developer's Guide
Scenario Overview
This solution should be implemented by the customer's application based on the E3Kit of virgin security and Chat Sdk. The E3Kit document link: Virtual gild E3Kit|Virtual gild Security.
Learn about the GroupEncryption-End-to-End-Encryption-E3Kit|Virtual Security documentation before reading the following solutions, especially the JWT token(JSON Web Token), create channel/create group, encrypt and decrypt messages.
Open application in virgin security console before using. This product is a paid product. See Pricing|Virgin Security for specific pricing.
The diagram below shows the high-level communication flow:
Products Applied
Basic Integration Guidelines
1.Chat SDK Initialization.
// 1. Get the `SDKAppID` from the Chat console.
// 2. Initialize the `config` object.
V2TIMSDKConfig config = new V2TIMSDKConfig();
// 3. Specify the log output level.
config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO);
// 4. Add the `V2TIMSDKListener` event listener. `sdkListener` is the implementation class of `V2TIMSDKListener`. If you don't need to listen to IM SDK events, skip this step.
V2TIMManager.getInstance().addIMSDKListener(sdkListener);
// 5. Initialize the IM SDK. You can call the login API as soon as you call this API.
V2TIMManager.getInstance().initSDK(context, sdkAppID, config);
2.E3Kit initialization, passing in the console configuration information, and generating jwt. Corresponding operation document link: GenerateClientTokens-GetStarted-E3Kit | VirgilSecurity.
The server generates jwttoken and sends it to the client.
// generate 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();
The Android client initializes the E3Kit, tokenCallback is the jwttoken returned by the above server, and User1 is the user ID.
// initialization 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);
Scenario-specific implementation
User Login
1.Call Chat Sdk login method for account login
String userID = "your user id";
//Generating UserSig
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) {
// The following error codes indicate an expired `userSig`, and you need to generate a new one for login again.
// 1. ERR_USER_SIG_EXPIRED (6206)
// 2. ERR_SVR_ACCOUNT_USERSIG_EXPIRED (70001)
// Note: Do not call the login API in case of other error codes; otherwise, the IM SDK may enter an infinite loop of login.
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
2.Call the eThree.register method to register the user to virgilSecurity. The corresponding operation document link: UserAuthentication-E3Kit | VirgilSecurity.
Note:User of im_ ID and user registered on virtilsecurity_ The id should be consistent
Start a one-to-one chat
1.Call the ethree.createRatchetChannel method in the E3Kit to create a one to one session (User1 and User2), and the corresponding operation document link: https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/double-ratchet/#create-channel
User1 creates a channel with User2
// create 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 can join the channel
// join 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
}
});
One-to-one chat message encryption and decryption
1.Use the channel created above to encrypt messages and link documents:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/double-ratchet/?#encrypt-and-decrypt-messages.
// one-to-one chat message encryption
// prepare a message
String messageToEncrypt = "Hello, User2!";
String encrypted = channel.encrypt(messageToEncrypt);
2.The encrypted message content is sent to Chat Sdk and sent with a customized message
// `msgID` returned by the API for on-demand use
String msgID = V2TIMManager.getInstance().sendC2CCustomMessage("virgil encrypted msg", "receiver_userID", new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
// The one-to-one text message sent successfully
}
@Override
public void onError(int code, String desc) {
// Failed to send the one-to-one text message
}
});
3.After the peer receiving the customized message
// Set the event listener
V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
/**
* Receive the custom one-to-one message
* @param msgID Message ID
* @param sender Sender information
* @param customData The sent content
*/
public void onRecvC2CCustomMessage(String msgID, V2TIMUserInfo sender, byte[] customData) {
Log.i("onRecvC2CCustomMessage", "msgID:" + msgID + ", from:" + sender.getNickName() + ", content:" + new String(customData));
//call E3Kit to decrypt msg
}
4.the peer calls E3Kit to decrypt and render it, as follows:
// Decrypt message
String decrypted = channel.decrypt(encrypted);
Start a channel(group)
1.Call the ethree.createGroup to create group,document link:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#create-group-chat.
// create group
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.If you need to add group members, the code below is as follows. You can call the remove method to delete a group member. Document link:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#add-new-participant.
// add group member
group.add(users.get("Den")).addCallback(new OnCompleteListener() {
@Override public void onSuccess() {
// Den was added!
}
@Override public void onError(@NotNull Throwable throwable) {
// Error handling
}
});
3.Call createGroup of Chat Sdk to create a group, joinGroup or inviteUserToGroup to add group members.
V2TIMManager.getInstance().createGroup(V2TIMManager.GROUP_TYPE_WORK, null, "groupA", new V2TIMValueCallback<String>() {
@Override
public void onSuccess(String s) {
// Group created successfully
}
@Override
public void onError(int code, String desc) {
// Failed to create the group
}
});
// Listen for the group creation notification
V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
@Override
public void onGroupCreated(String groupID) {
// A group was created. `groupID` is the ID of the created group.
}
});
// Invite the `userA` user to join the `groupA` group
List<String> userIDList = new ArrayList<>();
userIDList.add("userA");
V2TIMManager.getGroupManager().inviteUserToGroup("groupA", userIDList, new V2TIMValueCallback<List<V2TIMGroupMemberOperationResult>>() {
@Override
public void onSuccess(List<V2TIMGroupMemberOperationResult> v2TIMGroupMemberOperationResults) {
// Invited the user to the group successfully
}
@Override
public void onError(int code, String desc) {
// Failed to invite the user to the group
}
});
// Listen for the group invitation event
V2TIMManager.getInstance().addGroupListener(new V2TIMGroupListener() {
@Override
public void onMemberInvited(String groupID, V2TIMGroupMemberInfo opUser, List<V2TIMGroupMemberInfo> memberList) {
// A user was invited to the group. This callback can contain some UI tips.
}
});
Group chat message encryption and decryption
1.Use the group created above to encrypt messages and link documents:https://developer.virgilsecurity.com/docs/e3kit/end-to-end-encryption/group-chat/?#encrypt-and-decrypt-messages.
//Group message encryption
// prepare a message
String messageToEncrypt = "Hello, Bob and Carol!";
String encrypted = group.encrypt(messageToEncrypt);
2.The encrypted message content is sent to tencent Chat Sdk and sent with a customized message
String msgID = V2TIMManager.getInstance().sendGroupCustomMessage("virgil encrypted msg ".getBytes(), "groupID", V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
// The custom group message sent successfully
}
3.After the peer tencent cloud Chat Sdk receiving the customized message,
// Set the event listener
V2TIMManager.getInstance().addSimpleMsgListener(simpleMsgListener);
/**
* Receive the custom group message
* @param msgID Message ID
* @param groupID Group ID
* @param sender The group member information of the sender
* @param customData The sent content
*/
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));
//call E3Kit to decrypt msg
}
4.The peer decrypt and render it, as follows:
//Group message decryption
String decrypted = group.decrypt(encrypted, users.get("Alice"));
Note: After using this end-to-end encryption scheme, the local chat record search function of imsdk will not be available.
Order Now
Click here to quickly go to the purchase page to order.