{"id":19198554,"url":"https://github.com/virgilsecurity/demo-nexmo-chat-android","last_synced_at":"2025-05-09T01:19:13.695Z","repository":{"id":79478062,"uuid":"117533030","full_name":"VirgilSecurity/demo-nexmo-chat-android","owner":"VirgilSecurity","description":"A simple Web application that demonstrates how the end-to-end encryption works in chat messaging use case. The application uses Nexmo as a messaging provider.","archived":false,"fork":false,"pushed_at":"2018-08-29T22:49:38.000Z","size":256,"stargazers_count":3,"open_issues_count":2,"forks_count":2,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-20T10:41:53.598Z","etag":null,"topics":["android","chat","cryptography","e2ee","encryption","end-to-end-encryption","messenger","nexmo","secured-by-virgil","vonage","web-app"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VirgilSecurity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-15T10:46:12.000Z","updated_at":"2021-07-03T19:36:10.000Z","dependencies_parsed_at":"2023-03-13T20:09:57.523Z","dependency_job_id":null,"html_url":"https://github.com/VirgilSecurity/demo-nexmo-chat-android","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirgilSecurity%2Fdemo-nexmo-chat-android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirgilSecurity%2Fdemo-nexmo-chat-android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirgilSecurity%2Fdemo-nexmo-chat-android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirgilSecurity%2Fdemo-nexmo-chat-android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VirgilSecurity","download_url":"https://codeload.github.com/VirgilSecurity/demo-nexmo-chat-android/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253171556,"owners_count":21865358,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","chat","cryptography","e2ee","encryption","end-to-end-encryption","messenger","nexmo","secured-by-virgil","vonage","web-app"],"created_at":"2024-11-09T12:22:37.860Z","updated_at":"2025-05-09T01:19:13.684Z","avatar_url":"https://github.com/VirgilSecurity.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# End-to-End Encrypted Nexmo Android In-App Messaging App Demo\n\nThis readme walks you through the steps to bring the E2EE Nexmo In-App Messaging Android app to life. It also attempts to explain the key changes to the original [Nexmo code](https://github.com/Nexmo/messaging-demo-android) on GitHub.\n\n## What is End-to-End Encryption?\n\nFirst, let’s start with a quick refresher of what E2EE (End-to-End Encryption) is and how it works. E2EE is simple: when you type in a chat message, it gets encrypted on your mobile device (or in your browser) and gets decrypted only when your chat partner receives it and wants to display it in chat window.\n\n![Virgil Chat](https://github.com/VirgilSecurity/chat-back4app-android/blob/master/img/chat_example.png)\n*Note: image needs to be updated, it's directly referred from Back4App project*\n\nThe message remains encrypted while it travels over Wi-Fi and the Internet, through the cloud / web server, into a database, and on the way back to your chat partner. In other words, none of the networks or servers have a clue of what the two of you are chatting about.\n\n![Virgil Chat Server](https://github.com/VirgilSecurity/chat-back4app-android/blob/master/img/chat_example_server.png)\n*Note: image needs to be updated, it's directly referred from Back4App project*\n\nWhat’s difficult in End-to-End Encryption is the task of managing the encryption keys in a way that only the users involved in the chat can access them and nobody else. And when I write “nobody else”, I really mean it: even insiders of your cloud provider or even you, the developer, are out; [no accidental mistakes][_mistakes] or legally enforced peeking are possible. Writing crypto, especially for multiple platforms is hard: generating true random numbers, picking the right algorithms, and choosing the right encryption modes are just a few examples that make most developers wave their hands in the air and end up just NOT doing it.\n\nVirgil's End-to-End Encryption tech enables Nexmo developers to ignore all these annoying details and quickly and simply End-to-End Encrypt their users' In-App chat messages.\n\n**For an intro, this is how we’ll upgrade the Nexmo Android app to be End-to-End Encrypted:**\n1. During sign-up: we’ll generate the individual private \u0026 public keys for new users (remember: the recipient's public key encrypts messages and the matching recipient's private key decrypts them).\n1. Before sending messages, we’ll encrypt chat messages with the recipient's ever-changing public keys. Virgil's Perfect Forward Secrecy is the technology behind revolving encryption keys for every message: to make sure that future conversations are not compromised with a key that's accidentally leaked.\n1. After receiving messages, we’ll decrypt chat messages with the recipient's ever-changing private keys.\n\n![Virgil E2EE](https://github.com/VirgilSecurity/chat-back4app-android/blob/master/img/virgil_main.png)\n*Note: image needs to be updated, it's directly referred from Back4App project*\n\nWe’ll publish the users’ public keys to Virgil’s Cards Service so that chat users are able to look up each other and able to encrypt messages for each other.  The private keys will stay on the user devices.\n\n**OK, enough talking: let’s start doing!**\n\n- We’ll start by guiding you through the Android app’s setup,\n- Then, we’ll make you add the E2EE code and explain what each code block does.\n\n# Let's get set up!\n\n## Prerequisites\n\n* Java 7+\n* [Android Studio](https://developer.android.com/studio/index.html)\n* [Application API server](https://github.com/VirgilSecurity/demo-nexmo-server)\n\n## Sign up for Nexmo \u0026 Virgil accounts\n\n- Sign up for your Nexmo account\n- Any other steps here?\n- Sign up for a [Virgil Security account][_virgil_account]\n- Create a new app \u0026 token\n\n## Install Application API server\n\nApplication API server is already installed and available by the [link](https://auth-nexmo.virgilsecurity.com/)\n\n## Import Project in Android Studio:\n  - File -\u003e New -\u003e Project from Version Control -\u003e Git\n  - Git Repository URL: https://github.com/VirgilSecurity/demo-nexmo-android\n  - Check out the “master” branch\n\n### Configure mobile application\n\nOpen VirgilFacade class and define constants from the table below\n\n| Constant name | Description |\n| --- | --- |\n| VIRGIL_ACCESS_TOKEN | Your's Virgil Application access token. You should generate this token on the [dashboard](https://developer.virgilsecurity.com/account/dashboard/) or use the existing one |\n| VIRGIL_APP_PUBLIC_KEY | Your's Virgil Application public key as Base64-encoded string |\n| VIRGIL_AUTH_PUBLIC_KEY | Virgil Authentication server public key as Base64-encoded string |\n| AUTH_SERVER_URL | Application API server URL |\n\n# Code overview\n\n## Register new users\n\nTwo important terms here:\n\n  - **Virgil Key** – this is what we call a user's private key. Remember, private keys can decrypt data that was encrypted using the matching public key.\n  - **Virgil Card** – Virgil Сards carry the user’s public key. Virgil cards are published to Virgil’s Cards Service (imagine this service is like a telephone book) for other users to retrieve them: Alice needs to retrieve Bob’s public key in order to encrypt a message for Bob using that key. \n\nIn the E2EE version of the In-App Messaging app, we'll generate a Private Key for every user at signup time. We'll then generate the user's public key and publish it in a form of a new Virgil Card for the user, so that other users can find it and encrypt messages for us.\n\n```kotlin\n// Generate private key\nval virgilKey = virgilApi.keys.generate()\n\n// Create Virgil Card\nval customFields = HashMap\u003cString, String\u003e()\ncustomFields.put(\"deviceId\", Settings.Secure.ANDROID_ID)\nval virgilCard = virgilApi.getCards().create(userName, virgilKey,\n    \"name\", customFields)\n```\n\nTo create a Virgil Card, you'll need your Virgil Application's Private Key (otherwise, anybody can publish cards for your app without your control). Since you shouldn't store this key on mobile devices, we'll keep it in your web app and make your web app verify the users before card creation.\n\n```kotlin\nval csr = CSR(virgilCard.export())\nval response = NexmoApp.instance.serverClient.signup(csr).execute()\nvar registrationData = response.body()!!\n```\n\nregistrationData also contains JWT which should be used to login Nexmo with `ConversationClient`.\n\nYour mobile App is the only place where your Private Key is stored. So, you should store Private Key for future use. If you lose your Private Key, you won't be able to decrypt messages sent to you.\n\n```kotlin\nNexmoApp.instance.db.userDao().insert(User(registrationData.user.id,\n    userName, registrationData.user.href, createdVirgilCard.id, \n    registrationData.user.virgilCard, virgilKey.privateKey.value))\n```\n\nInitialize `SecureChat` and generate one-time keys for future use.\n\n```kotlin\ncrypto = VirgilCrypto()\n\nkeyStorage = JsonFileKeyStorage(\n    context.getFilesDir().getAbsolutePath(), userName + \".ks\")\nuserDataStorage = JsonFileUserDataStorage(\n    context.getFilesDir().getAbsolutePath(), userName + \".ds\")\n\n// Configure PFS\nvar chatContext = SecureChatContext(virgilCard, privateKey,\n    crypto, VIRGIL_ACCESS_TOKEN)\nchatContext.keyStorage = keyStorage\nchatContext.deviceManager = DefaultDeviceManager()\nchatContext.userDataStorage = userDataStorage\n\nsecureChat = SecureChat(chatContext)\nsecureChat?.rotateKeys(10)\n```\n\n## Login\n\nUpon login, we obtain a Virgil authentication token from the server. See the flow details by the [link](https://github.com/VirgilSecurity/virgil-services-auth).\n\n```kotlin\n// Get challenge message\nval challengeMessage = this.authClient.getChallengeMessage(cardId)\n\n// Decode encrypted message\nval decodedMessage = this.crypto.decrypt(\n    ConvertionUtils.base64ToBytes(challengeMessage.encryptedMessage),\n    this.privateKey)\n\n// Encrypt decoded message with application public key\nval appPublicKey = this.crypto.importPublicKey(\n    ConvertionUtils.base64ToBytes(VIRGIL_AUTH_PUBLIC_KEY))\n\nval newEncryptedMessage =\n    this.crypto.encrypt(decodedMessage, appPublicKey)\n\nval message = ConvertionUtils.toBase64String(newEncryptedMessage)\n\n// Send acknowledge to auth server\nval code = this.authClient.acknowledge(\n    challengeMessage.authorizationGrantId, message)\n\n// Obtain access token\nval accessTokenResponse = this.authClient.obtainAccessToken(code)\nval virgilToken = accessTokenResponse.accessToken\n```\n\nLogin Nexmo with `ConversationClient`.\n\n```kotlin\nval response = NexmoApp.instance.serverClient\n    .jwt(\"Bearer ${virgilToken}\").execute()\n    \nval jwt = response.body()!!.jwt\n```\n\n## Load users\n\nLet's load the list of registered users.\n\n```kotlin\nval virgilToken = VirgilFacade.instance.getVirgilToken()\nval response = NexmoApp.instance.serverClient\n    .getUsers(\"Bearer ${virgilToken}\").execute()\nvar users = response.body()\n```\n\nStart a conversation.\n\n```kotlin\nconversationClient.newConversation(true, userName,\n    object : RequestHandler\u003cConversation\u003e {\n\n    override fun onError(apiError: NexmoAPIError?) {\n        closeWithError(\"Conversation is not created\", apiError)\n    }\n\n    override fun onSuccess(result: Conversation?) {\n        Log.d(TAG, \"Created conversation ${result?.conversationId} for user ${userName}\")\n        mConversation = result\n        mConversation?.invite(userName, object : RequestHandler\u003cMember\u003e {\n            override fun onError(apiError: NexmoAPIError?) {\n                closeWithError(\"Can't invite user ${userName} into conversation\", apiError)\n            }\n\n            override fun onSuccess(result: Member?) {\n                Log.d(TAG, \"User ${result?.name} invited into conversation\")\n                mMemberCard = VirgilFacade.instance.virgilApi.cards.find(result?.name).firstOrNull()?.model\n                \n                // initizlize conversation\n                ...\n            }\n        })\n    }\n})\n```\n\nYou'll need a Virgil Card of the user you are starting conversation with.\n\n```kotlin\nval userName = NexmoUtils.getConversationPartner(mConversation!!)?.name\nmMemberCard = VirgilFacade.instance.virgilApi.cards.find(userName).firstOrNull()?.model\n```\n\nNow you can send and receive messages.\n\n### Sending message\n\n#### Encrypting message\n\n```kotlin\n// Get active session\nvar secureSession = secureChat!!.activeSession(recipientCard.getId());\n\n// If no session, start a new one\nif (secureSession == null) {\n    secureSession = secureChat!!.startNewSession(recipientCard, null);\n}\n\n// Encrypt message text\nval encryptedText = secureSession.encrypt(text);\n```\n\n#### Send a message to the conversation\n\nYou can't decrypt message that you encrypted. Therefore, you should store the original message locally. To ensure that the message isn't tampered, create a hash code from the encrypted text.\n\n```kotlin\nmConversation?.sendText(encryptedMessage,\n    object : RequestHandler\u003cEvent\u003e {\n\n    override fun onSuccess(result: Event?) {\n        // Save message in database\n        val hash = encryptedMessage.hashCode().toString()\n        val msg = Message(hash, mConversation!!.conversationId, result!!.member.userId, text)\n        messageDao.insert(msg)\n    }\n\n    override fun onError(apiError: NexmoAPIError?) {\n        Log.e(TAG, \"Send message error\", apiError)\n    }\n})\n```\n\n### Receiving message\n\n#### Decrypting message\n\nLet's identify the message sender first.\n\n```kotlin\nif (conversationClient.user.userId.equals(textMessage.member.userId)) {\n    // This message was sent by myself. Find in database\n    ....\n} else {\n   // Message from another conversation member\n   ...\n}\n```\n\nIf it's your own message, just get it from the database by the encrypted text hash code.\n\n```kotlin\nval hash = textMessage.text.hashCode().toString()\nval message = messageDao.getMessage(mConversation!!.conversationId, hash)\nval decryptedText = message.text\n```\n\nIf the message is sent by somebody else, let's decrypt it.\n\n```kotlin\n// Loadup user session\nvar secureSession = secureChat!!.loadUpSession(senderCard, encryptedMessage, null)\nval decryptedText = secureSession.decrypt(encryptedMessage)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvirgilsecurity%2Fdemo-nexmo-chat-android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvirgilsecurity%2Fdemo-nexmo-chat-android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvirgilsecurity%2Fdemo-nexmo-chat-android/lists"}