Diese Readme führt Sie durch die Schritte, um die Android-App der E2EE Nexmo In-App-Nachrichten zum Leben zu erwecken. Es wird auch versucht, die wichtigsten Änderungen des ursprünglichen NEXMO -Code auf GitHub zu erklären.
Beginnen wir zunächst mit einer kurzen Auffrischung dessen, was E2EE (End-to-End-Verschlüsselung) ist und wie es funktioniert. E2EE ist einfach: Wenn Sie eine Chat -Nachricht eingeben, wird es auf Ihrem mobilen Gerät (oder in Ihrem Browser) verschlüsselt und erst dann entschlüsselt, wenn Ihr Chat -Partner es empfängt und sie im Chat -Fenster anzeigen möchte.
Hinweis: Das Bild muss aktualisiert werden, es wird direkt vom Back4App -Projekt verwiesen
Die Nachricht bleibt verschlüsselt, während sie über Wi-Fi und das Internet, über den Cloud / Web-Server, in eine Datenbank und auf dem Rückweg zu Ihrem Chat-Partner umsetzt. Mit anderen Worten, keines der Netzwerke oder Server hat einen Hinweis darauf, worüber Sie beide unterhalten.
Hinweis: Das Bild muss aktualisiert werden, es wird direkt vom Back4App -Projekt verwiesen
Was bei der End-to-End-Verschlüsselung schwierig ist, ist die Aufgabe, die Verschlüsselungsschlüssel so zu verwalten, dass nur die am Chat beteiligten Benutzer auf sie zugreifen können, und sonst niemanden. Und wenn ich „niemand anderes“ schreibe, meine ich es wirklich: sogar Insider Ihres Cloud -Anbieters oder sogar Sie, der Entwickler, sind raus; [Es sind keine zufälligen Fehler] [_ Fehler] oder legal erzwungenes Peeking sind möglich. Das Schreiben von Krypto, insbesondere für mehrere Plattformen, ist schwierig: Es sind nur einige Beispiele, die die meisten Entwickler dazu bringen, ihre Hände in die Luft zu winken und am Ende einfach nicht zu tun, um die richtigen Algorithmen zu wählen und die richtigen Verschlüsselungsmodi zu wählen.
Mit Virgils End-to-End-Verschlüsselungstechnologie können NexMO-Entwickler all diese nervigen Details ignorieren und die In-App-Chat-Nachrichten ihrer Benutzer schnell und einfach verschlüsseln.
Für ein Intro werden wir die Nexmo Android-App so aufrüsten, dass sie end-to-End-verschlüsselt werden:
Hinweis: Das Bild muss aktualisiert werden, es wird direkt vom Back4App -Projekt verwiesen
Wir veröffentlichen die öffentlichen Schlüssel der Benutzer für den Kartendienst von Virgil, damit Chat -Benutzer sich gegenseitig nachsehen und Nachrichten für einander verschlüsseln können. Die privaten Schlüssel bleiben auf den Benutzergeräten.
Ok, genug reden: Lass uns anfangen!
Anwendungs -API -Server ist bereits installiert und über den Link verfügbar
Öffnen Sie die Virgilfacade -Klasse und definieren Sie Konstanten aus der folgenden Tabelle
| Konstanter Name | Beschreibung |
|---|---|
| Virgil_access_token | Ihr Virgil Application Access Token. Sie sollten dieses Token auf dem Dashboard generieren oder die vorhandene verwenden |
| Virgil_app_public_key | Die Virgil-Anwendung öffentlicher Schlüssel als Basis64-kodierter Zeichenfolge |
| Virgil_auth_public_key | Virgil Authentication Server öffentlicher Schlüssel als Basis64-kodierter Zeichenfolge |
| Auth_server_url | Anwendungs -API -Server -URL |
Zwei wichtige Begriffe hier:
In der E2EE-Version der In-App-Messaging-App generieren wir zum Anmeldezeit einen privaten Schlüssel für jeden Benutzer. Wir werden dann den öffentlichen Schlüssel des Benutzers generieren und ihn in Form einer neuen Virgil -Karte für den Benutzer veröffentlichen, damit andere Benutzer sie finden und Nachrichten für uns verschlüsseln können.
// 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)Um eine Virgil -Karte zu erstellen, benötigen Sie den privaten Schlüssel Ihrer Virgil -Anwendung (ansonsten kann jeder Karten für Ihre App ohne Ihre Kontrolle veröffentlichen). Da Sie diesen Schlüssel nicht auf mobilen Geräten speichern sollten, behalten wir ihn in Ihrer Web -App auf und lassen Ihre Web -App die Benutzer vor der Kartenerstellung überprüfen.
val csr = CSR (virgilCard.export())
val response = NexmoApp .instance.serverClient.signup(csr).execute()
var registrationData = response.body() !! RegistrationData enthält auch JWT, mit dem Nexmo mit ConversationClient angemeldet werden sollte.
Ihre mobile App ist der einzige Ort, an dem Ihr privater Schlüssel gespeichert ist. Sie sollten also einen privaten Schlüssel für die zukünftige Nutzung speichern. Wenn Sie Ihren privaten Schlüssel verlieren, können Sie keine an Sie gesendeten Nachrichten entschlüsseln.
NexmoApp .instance.db.userDao().insert( User (registrationData.user.id,
userName, registrationData.user.href, createdVirgilCard.id,
registrationData.user.virgilCard, virgilKey.privateKey.value)) Initialisieren Sie SecureChat und generieren Sie einmalige Schlüssel für die zukünftige Verwendung.
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 )Bei der Anmeldung erhalten wir ein Virgil -Authentifizierungs -Token vom Server. Siehe die Flussdetails im Link.
// 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 Login Nexmo mit ConversationClient .
val response = NexmoApp .instance.serverClient
.jwt( " Bearer ${virgilToken} " ).execute()
val jwt = response.body() !! .jwtLaden wir die Liste der registrierten Benutzer.
val virgilToken = VirgilFacade .instance.getVirgilToken()
val response = NexmoApp .instance.serverClient
.getUsers( " Bearer ${virgilToken} " ).execute()
var users = response.body()Beginnen Sie ein Gespräch.
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
.. .
}
})
}
})Sie benötigen eine Virgil -Karte des Benutzers, mit dem Sie sich unterhalten.
val userName = NexmoUtils .getConversationPartner(mConversation !! )?.name
mMemberCard = VirgilFacade .instance.virgilApi.cards.find(userName).firstOrNull()?.modelJetzt können Sie Nachrichten senden und empfangen.
// 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);Sie können keine Nachricht entschlüsseln, die Sie verschlüsselt haben. Daher sollten Sie die ursprüngliche Nachricht lokal speichern. Um sicherzustellen, dass die Nachricht nicht manipuliert wird, erstellen Sie einen Hash -Code aus dem verschlüsselten 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)
}
})Lassen Sie uns zuerst den Nachrichtensender identifizieren.
if (conversationClient.user.userId.equals(textMessage.member.userId)) {
// This message was sent by myself. Find in database
....
} else {
// Message from another conversation member
.. .
}Wenn es sich um Ihre eigene Nachricht handelt, erhalten Sie sie einfach aus der Datenbank durch den verschlüsselten Text -Hash -Code.
val hash = textMessage.text.hashCode().toString()
val message = messageDao.getMessage(mConversation !! .conversationId, hash)
val decryptedText = message.textWenn die Nachricht von jemand anderem gesendet wird, lassen Sie uns sie entschlüsseln.
// Loadup user session
var secureSession = secureChat !! .loadUpSession(senderCard, encryptedMessage, null )
val decryptedText = secureSession.decrypt(encryptedMessage)