このREADMEは、E2EE Nexmo In-AppメッセージングAndroidアプリを実現するための手順を説明します。また、GitHubの元のNEXMOコードの重要な変更を説明しようとします。
まず、E2EE(エンドツーエンドの暗号化)とそれがどのように機能するかについての簡単な復習から始めましょう。 E2EEはシンプルです。チャットメッセージを入力すると、モバイルデバイス(またはブラウザ)で暗号化され、チャットパートナーが受信してチャットウィンドウに表示したい場合にのみ復号化されます。
注:画像を更新する必要があり、Back4Appプロジェクトから直接参照されます
メッセージは、Wi-Fiとインターネット、クラウド / Webサーバーを介して、データベースに移動し、チャットパートナーに戻る途中で移動している間、暗号化されたままです。言い換えれば、ネットワークやサーバーのどれも、二人がチャットしていることの手がかりを持っていません。
注:画像を更新する必要があり、Back4Appプロジェクトから直接参照されます
エンドツーエンドの暗号化で難しいのは、チャットに関与するユーザーだけがそれらにアクセスでき、他の誰にもアクセスできないように、暗号化キーを管理するタスクです。そして、私が「他の誰もいない」と書くとき、私は本当にそれを意味します:あなたのクラウドプロバイダーやあなた、さらには開発者のインサイダーでさえ、出ています。 [偶発的な間違いはありません] [_ミス]または法的に施行された覗き見が可能です。特に複数のプラットフォームで暗号を書くことは困難です。真の乱数を生成し、適切なアルゴリズムを選択し、適切な暗号化モードを選択することは、ほとんどの開発者が空中で手を振ってそれを行わないようにするいくつかの例です。
Virgilのエンドツーエンドの暗号化技術により、NEXMO開発者はこれらすべての迷惑な詳細を無視し、ユーザーのアプリ内チャットメッセージを迅速かつ簡単に暗号化できます。
イントロの場合、これがNEXMO Androidアプリをアップグレードしてエンドツーエンドの暗号化された方法です。
注:画像を更新する必要があり、Back4Appプロジェクトから直接参照されます
ユーザーのパブリックキーをVirgilのカードサービスに公開して、チャットユーザーがお互いを検索し、お互いにメッセージを暗号化できるようにします。プライベートキーはユーザーデバイスにとどまります。
わかりました、十分に話してください:始めましょう!
アプリケーションAPIサーバーは既にインストールされており、リンクで利用可能です
Virgilfacadeクラスを開き、下の表から定数を定義します
| 定数名 | 説明 |
|---|---|
| virgil_access_token | あなたのバージルアプリケーションアクセストークン。ダッシュボードでこのトークンを生成するか、既存のトークンを使用する必要があります |
| virgil_app_public_key | Base64エンコード文字列としてのVirgil Application公開キー |
| Virgil_auth_public_key | Virgil Authentication Server Base64エンコード文字列としての公開キー |
| auth_server_url | アプリケーションAPIサーバーURL |
ここでの2つの重要な用語:
アプリ内メッセージングアプリの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の秘密鍵が必要です(そうでない場合、誰でもあなたのコントロールなしでアプリのカードを公開できます)。このキーをモバイルデバイスに保存するべきではないため、Webアプリに保持し、カードの作成前にWebアプリにユーザーを確認させます。
val csr = CSR (virgilCard.export())
val response = NexmoApp .instance.serverClient.signup(csr).execute()
var registrationData = response.body() !! RegistrationDataにはJWTも含まれています。JWTには、 ConversationClientでNexmoをログインするために使用する必要があります。
あなたのモバイルアプリは、あなたの秘密鍵が保存される唯一の場所です。したがって、将来の使用のために秘密鍵を保存する必要があります。秘密鍵を失った場合、送信されたメッセージを復号化することはできません。
NexmoApp .instance.db.userDao().insert( User (registrationData.user.id,
userName, registrationData.user.href, createdVirgilCard.id,
registrationData.user.virgilCard, virgilKey.privateKey.value)) SecureChatを初期化し、将来の使用のために1回限りのキーを生成します。
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 ConversationClientでNexmoをログインします。
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
.. .
}
})
}
})会話を始めているユーザーのヴァージルカードが必要です。
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)