이 README는 E2EE Nexmo 인앱 메시징 Android 앱을 생생하게하는 단계를 안내합니다. 또한 GitHub의 원래 Nexmo 코드의 주요 변경 사항을 설명하려고합니다.
먼저 E2EE (엔드 투 엔드 암호화)의 빠른 새로 고침과 작동 방식을 빠르게 새롭게 시작하겠습니다. E2EE는 간단합니다. 채팅 메시지를 입력하면 모바일 장치 (또는 브라우저)에서 암호화되고 채팅 파트너가 수신하고 채팅 창에 표시하려는 경우에만 해독됩니다.
참고 : 이미지를 업데이트해야하며 Back4App 프로젝트에서 직접 참조됩니다.
메시지는 Wi-Fi 및 인터넷을 통해 클라우드 / 웹 서버, 데이터베이스로, 채팅 파트너로 돌아 오는 동안 암호화 된 상태로 유지됩니다. 다시 말해, 네트워크 나 서버 중 어느 것도 두 사람이 채팅하는 것에 대한 단서가 없습니다.
참고 : 이미지를 업데이트해야하며 Back4App 프로젝트에서 직접 참조됩니다.
엔드 투 엔드 암호화에서 어려운 것은 채팅에 관련된 사용자 만 액세스 할 수있는 방식으로 암호화 키를 관리하는 작업입니다. 그리고 내가“다른 사람”을 쓸 때, 나는 정말로 그것을 의미합니다. 클라우드 제공 업체의 내부자조차도 개발자도 나가고 있습니다. [우발적 인 실수 없음] [_ 실수] 또는 법적으로 시행되는 엿보기가 가능합니다. Crypto를 작성하는 것은 특히 여러 플랫폼의 경우 어렵습니다. 실제 랜덤 숫자를 생성하고 올바른 알고리즘을 선택하며 올바른 암호화 모드를 선택하는 것은 대부분의 개발자가 공중에서 손을 흔들고 결국 그렇게하지 않는 몇 가지 예일뿐입니다.
Virgil의 엔드 투 엔드 암호화 기술을 통해 Nexmo 개발자는 이러한 성가신 세부 사항을 모두 무시하고 사용자의 인앱 내 채팅 메시지를 빠르고 간단하게 암호화 할 수 있습니다.
인트로의 경우 이것이 우리가 Nexmo Android 앱을 업그레이드하여 엔드 투 엔드 암호화로 업그레이드하는 방법입니다.
참고 : 이미지를 업데이트해야하며 Back4App 프로젝트에서 직접 참조됩니다.
채팅 사용자가 서로를 찾고 서로 메시지를 암호화 할 수 있도록 사용자의 공개 키를 Virgil의 카드 서비스에 게시하겠습니다. 개인 키는 사용자 장치에 유지됩니다.
좋아, 충분한 이야기 : 시작하자!
Application API 서버는 이미 설치되어 링크에서 사용할 수 있습니다.
VirgilFacade 클래스를 열고 아래 표에서 상수를 정의하십시오.
| 상수 이름 | 설명 |
|---|---|
| virgil_access_token | 귀하의 Virgil Application Access Token. 대시 보드 에서이 토큰을 생성하거나 기존 토큰을 사용해야합니다. |
| virgil_app_public_key | 귀하의 Virgil Application Public Key는 Base64에 인코딩 된 문자열입니다 |
| virgil_auth_public_key | Virgil Authentication Server 공개 키는 Base64 인코딩 된 문자열입니다 |
| auth_server_url | 응용 프로그램 API 서버 URL |
여기에 두 가지 중요한 용어 :
인앱 메시징 앱의 E2EE 버전에서는 가입 시간에 모든 사용자에 대한 개인 키를 생성합니다. 그런 다음 사용자의 공개 키를 생성하고 사용자를위한 새로운 Virgil 카드 형식으로 게시하여 다른 사용자가 찾아 메시지를 암호화 할 수 있도록합니다.
// Generate private key
val virgilKey = virgilApi.keys.generate()
// Create Virgil Card
val customFields = HashMap < String , String >()
customFields.put( " deviceId " , Settings . Secure . ANDROID_ID )
val virgilCard = virgilApi.getCards().create(userName, virgilKey,
" name " , customFields)Virgil 카드를 만들려면 Virgil Application의 개인 키가 필요합니다 (그렇지 않으면 누구나 제어하지 않고 앱에 대한 카드를 게시 할 수 있습니다). 이 키를 모바일 장치에 저장해서는 안되므로 웹 앱에 보관하고 웹 앱이 카드 생성 전에 사용자를 확인하게합니다.
val csr = CSR (virgilCard.export())
val response = NexmoApp .instance.serverClient.signup(csr).execute()
var registrationData = response.body() !! RegistrationData에는 JWT가 포함되어 있으며 Nexmo를 ConversationClient 로 로그인하는 데 사용해야합니다.
모바일 앱은 개인 키가 저장되는 유일한 장소입니다. 따라서 향후 사용하기 위해 개인 키를 저장해야합니다. 개인 키를 잃으면 메시지를 해독 할 수 없습니다.
NexmoApp .instance.db.userDao().insert( User (registrationData.user.id,
userName, registrationData.user.href, createdVirgilCard.id,
registrationData.user.virgilCard, virgilKey.privateKey.value)) SecureChat 초기화하고 향후 사용하기 위해 일회성 키를 생성하십시오.
crypto = VirgilCrypto ()
keyStorage = JsonFileKeyStorage (
context.getFilesDir().getAbsolutePath(), userName + " .ks " )
userDataStorage = JsonFileUserDataStorage (
context.getFilesDir().getAbsolutePath(), userName + " .ds " )
// Configure PFS
var chatContext = SecureChatContext (virgilCard, privateKey,
crypto, VIRGIL_ACCESS_TOKEN )
chatContext.keyStorage = keyStorage
chatContext.deviceManager = DefaultDeviceManager ()
chatContext.userDataStorage = userDataStorage
secureChat = SecureChat (chatContext)
secureChat?.rotateKeys( 10 )로그인하면 서버에서 Virgil 인증 토큰을 얻습니다. 링크의 흐름 세부 사항을 참조하십시오.
// Get challenge message
val challengeMessage = this .authClient.getChallengeMessage(cardId)
// Decode encrypted message
val decodedMessage = this .crypto.decrypt(
ConvertionUtils .base64ToBytes(challengeMessage.encryptedMessage),
this .privateKey)
// Encrypt decoded message with application public key
val appPublicKey = this .crypto.importPublicKey(
ConvertionUtils .base64ToBytes( VIRGIL_AUTH_PUBLIC_KEY ))
val newEncryptedMessage =
this .crypto.encrypt(decodedMessage, appPublicKey)
val message = ConvertionUtils .toBase64String(newEncryptedMessage)
// Send acknowledge to auth server
val code = this .authClient.acknowledge(
challengeMessage.authorizationGrantId, message)
// Obtain access token
val accessTokenResponse = this .authClient.obtainAccessToken(code)
val virgilToken = accessTokenResponse.accessToken Nexmo를 ConversationClient 로 로그인하십시오.
val response = NexmoApp .instance.serverClient
.jwt( " Bearer ${virgilToken} " ).execute()
val jwt = response.body() !! .jwt등록 된 사용자 목록을로드합시다.
val virgilToken = VirgilFacade .instance.getVirgilToken()
val response = NexmoApp .instance.serverClient
.getUsers( " Bearer ${virgilToken} " ).execute()
var users = response.body()대화를 시작하십시오.
conversationClient.newConversation( true , userName,
object : RequestHandler < Conversation > {
override fun onError ( apiError : NexmoAPIError ? ) {
closeWithError( " Conversation is not created " , apiError)
}
override fun onSuccess ( result : Conversation ? ) {
Log .d( TAG , " Created conversation ${result?.conversationId} for user ${userName} " )
mConversation = result
mConversation?.invite(userName, object : RequestHandler < Member > {
override fun onError ( apiError : NexmoAPIError ? ) {
closeWithError( " Can't invite user ${userName} into conversation " , apiError)
}
override fun onSuccess ( result : Member ? ) {
Log .d( TAG , " User ${result?.name} invited into conversation " )
mMemberCard = VirgilFacade .instance.virgilApi.cards.find(result?.name).firstOrNull()?.model
// initizlize conversation
.. .
}
})
}
})대화를 시작하는 사용자의 Virgil 카드가 필요합니다.
val userName = NexmoUtils .getConversationPartner(mConversation !! )?.name
mMemberCard = VirgilFacade .instance.virgilApi.cards.find(userName).firstOrNull()?.model이제 메시지를 보내고받을 수 있습니다.
// Get active session
var secureSession = secureChat !! .activeSession(recipientCard.getId());
// If no session, start a new one
if (secureSession == null ) {
secureSession = secureChat !! .startNewSession(recipientCard, null );
}
// Encrypt message text
val encryptedText = secureSession.encrypt(text);암호화 된 메시지를 해독 할 수 없습니다. 따라서 원본 메시지를 로컬로 저장해야합니다. 메시지가 변조되지 않도록하려면 암호화 된 텍스트에서 해시 코드를 만듭니다.
mConversation?.sendText(encryptedMessage,
object : RequestHandler < Event > {
override fun onSuccess ( result : Event ? ) {
// Save message in database
val hash = encryptedMessage.hashCode().toString()
val msg = Message (hash, mConversation !! .conversationId, result !! .member.userId, text)
messageDao.insert(msg)
}
override fun onError ( apiError : NexmoAPIError ? ) {
Log .e( TAG , " Send message error " , apiError)
}
})먼저 메시지 발신자를 확인해 봅시다.
if (conversationClient.user.userId.equals(textMessage.member.userId)) {
// This message was sent by myself. Find in database
....
} else {
// Message from another conversation member
.. .
}자신의 메시지라면 암호화 된 텍스트 해시 코드로 데이터베이스에서 가져 오십시오.
val hash = textMessage.text.hashCode().toString()
val message = messageDao.getMessage(mConversation !! .conversationId, hash)
val decryptedText = message.text메시지를 다른 사람이 전송하면 해독합시다.
// Loadup user session
var secureSession = secureChat !! .loadUpSession(senderCard, encryptedMessage, null )
val decryptedText = secureSession.decrypt(encryptedMessage)