此錄像機將帶您瀏覽E2EE Nexmo應用程序內消息Android應用程序的步驟。它還試圖解釋GitHub上原始Nexmo代碼的關鍵更改。
首先,讓我們從快速重新審查E2EE(端到端加密)及其工作原理。 E2EE很簡單:當您輸入聊天消息時,它會在移動設備(或瀏覽器中)加密,並且僅當您的聊天夥伴收到聊天夥伴並希望在聊天窗口中顯示它時才被解密。
注意:需要更新圖像,它直接從Back4App項目中引用
該消息在Wi-Fi和Internet,通過雲 / Web服務器,數據庫中以及返回您的聊天合作夥伴的途中保持加密。換句話說,網絡或服務器都沒有你們兩個正在聊天的線索。
注意:需要更新圖像,它直接從Back4App項目中引用
端到端加密困難的是,只有聊天中涉及的用戶才能訪問它們而沒有其他人,以管理加密密鑰的任務。當我寫“沒有其他人”時,我真的是說:即使是您的雲提供商的內部人士,甚至您,開發人員也都出去了; [沒有意外錯誤] [_錯誤]或合法執行的窺視是可能的。編寫加密貨幣,尤其是對於多個平台來說很難:生成真實的隨機數,選擇正確的算法以及選擇正確的加密模式只是幾個示例,這些示例使大多數開發人員在空中揮舞著雙手,最終不這樣做。
Virgil的端到端加密技術使Nexmo開發人員能夠忽略所有這些煩人的細節,並迅速而簡單地端到端加密用戶的應用程序內聊天消息。
對於介紹,這就是我們將如何升級Nexmo Android應用程序進行端到端加密:
注意:需要更新圖像,它直接從Back4App項目中引用
我們將發布用戶對Virgil卡服務的公共密鑰,以便聊天用戶能夠互相查找並能夠相互加密消息。私鑰將留在用戶設備上。
好的,足夠說話:讓我們開始做吧!
應用程序API服務器已經安裝,鏈接可用
開放virgilfacade類並從下表定義常數
| 恆定名稱 | 描述 |
|---|---|
| virgil_access_token | 您的Virgil應用程序訪問令牌。您應該在儀表板上生成這個令牌或使用現有的令牌 |
| virgil_app_public_key | 您的virgil應用程序公鑰為base64編碼的字符串 |
| virgil_auth_public_key | Virgil身份驗證服務器公共密鑰作為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應用程序的私鑰(否則,任何人都可以在不受控制的情況下發布您的應用程序卡)。由於您不應該將此密鑰存儲在移動設備上,因此我們將其保存在您的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並生成一次性鍵供將來使用。
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
.. .
}
})
}
})您需要與您開始對話的用戶的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)