엔드 투 엔드 암호화 채팅 구현하기: Virgil E3Kit 및 Tencent Chat SDK를 이용한 개발자 가이드

10 분 읽기
Feb 18, 2025

시나리오 개요

이 솔루션은 Virgin Security의 E3Kit과 Chat SDK를 기반으로 고객의 애플리케이션에 구현되어야 합니다. E3Kit 문서 링크: Virtual gild E3Kit|Virtual gild Security.

다음 솔루션을 읽기 전에 GroupEncryption-End-to-End-Encryption-E3Kit|Virtual Security 문서를 학습하십시오. 특히 JWT 토큰(구조적 웹 토큰), 채널 생성/그룹 생성, 메시지 암호화 및 복호화에 대해 알아보십시오.

사용하기 전에 Virgin Security 콘솔에서 애플리케이션을 여십시오. 이 제품은 유료 제품입니다. 특정 가격에 대한 내용은 Pricing|Virgin Security.

설명: 이 이미지는 '라는 이름의 애플리케이션 대시보드 인터페이스를 보여줍니다.

아래 다이어그램은 고수준의 통신 흐름을 보여줍니다:

다이어그램은 사용자, 그들의 서버, Tencent 서버 간의 고수준 통신 흐름을 설명합니다.

적용된 제품

채팅

기본 통합 가이드라인

1. Chat SDK 초기화.

// 1. Chat 콘솔에서 `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 초기화
// 필수 매개변수인 identity, tokenCallback 및 context로 EThreeParams 생성
EThreeParams params = new EThreeParams("User1",
   tokenCallback,context);
// EThreeParams로 E3Kit을 초기화합니다.
EThree ethree = new EThree(params);

시나리오별 구현

사용자 로그인

1. 계정 로그인을 위해 Chat Sdk 로그인 메서드를 호출합니다.

String userID = "your user 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`를 나타내며, 로그인하기 위해 새로 생성해야 합니다.
       // 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. E3Kit에서 ethree.createRatchetChannel 메서드를 호출하여 일대일 세션(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에 보내고 사용자 정의 메시지와 함께 전송합니다.

// API에서 반환된 `msgID`를 온디맨드 방식으로 사용
String msgID = V2TIMManager.getInstance().sendC2CCustomMessage("virgil encrypted msg", "receiver_userID", 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 + ", from:" + sender.getNickName() + ", content:" + 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. 암호화된 메시지 내용을 Tencent Chat Sdk에 보내고 사용자 정의 메시지와 함께 전송합니다.

String msgID = V2TIMManager.getInstance().sendGroupCustomMessage("virgil encrypted msg ".getBytes(), "groupID", V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onSuccess(V2TIMMessage message) {
    // 사용자 정의 그룹 메시지가 성공적으로 전송되었습니다.
}

3. 상대방이 Tencent Cloud 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 + ", from:" + sender.getNickName() + ", content:" + new String(customData));
// E3Kit을 호출하여 메시지 복호화
}

4. 상대방이 복호화하고 렌더링합니다:

// 그룹 메시지 복호화
String decrypted = group.decrypt(encrypted, users.get("Alice"));

참고: 이 종단 간 암호화 체계를 사용한 후에는 imsdk의 로컬 채팅 기록 검색 기능을 사용할 수 없습니다.

지금 주문하기

여기를 클릭하여 구매 페이지로 빠르게 이동하여 주문하십시오.