Encrypted Chat on iOS (Swift)
Tags: ios, swift, chat, encrypted
Published: 2020-05-08 18:52:22

---
title: Encrypted Chat on iOS (Swift)
published: true
description: Tutorial on how to build an encrypted chat app for iOS (Swift) using Stream and Virgil Security
tags: ios, swift, chat, encrypted
cover_image: https://cdn-images-1.medium.com/max/1000/1*56NOSolhVJ3dDe1zM4iLkQ.jpeg
canonical_url: https://getstream.io/blog/encrypted-chat-ios-swift/
---

In this tutorial, we'll build encrypted chat on iOS using Swift. We'll combine [Stream Chat](https://getstream.io/chat/) and [Virgil Security](https://virgilsecurity.com/). Both Stream Chat and Virgil make it easy to create a solution with high security with all the features you expect. These two services allow developers to integrate chat that is zero-knowledge. The application embeds Virgil Security's [E3Kit](https://github.com/VirgilSecurity/virgil-e3kit-x) with [Stream Chat's Swift](https://github.com/GetStream/stream-chat-swift) components.

> **Note**: All source code for this application is available on [GitHub](https://github.com/nparsons08/stream-ios-encrypted-chat).

## What is end-to-end encryption?

End-to-end encryption means that two people can have trade messages via the internet without anyone else being able to read them, even if the transmission or storage is compromised. To do this, the app encrypts the message before it leaves a user's device, and only the intended recipient can decrypt the message.

Virgil Security is a vendor that allows us to create end-to-end encryption via public/private key technology. Virgil provides a platform that allows us to create, store, and offer robust end-to-end encryption securely.

During this tutorial, we will create a Stream Chat app that uses Virgil's encryption to prevent anyone except the intended parties from reading messages. No one in your company, nor any cloud provider you use, can read these messages. Even if a malicious person gained access to the database containing them, all they would see is encrypted text, called ciphertext.

## Building an Encrypted Chat Application

To build this application, we'll mostly rely on a few libraries from Stream Chat and Virgil (please check out the dependencies in the source to see what versions). Our final product will encrypt text on the device before sending a message. Decryption and verification will both happen in the receiver's device. Stream's Chat API will only see ciphertext, ensuring our user's data is never seen by anyone else, including you.

To accomplish this, the app performs the following process:

* A user authenticates with your backend.
* The iOS app requests a Stream auth token and API key from the `backend`. The Swift app creates a [Stream Chat Client](https://getstream.io/chat/docs/#init_and_users) for that user.
* The mobile app requests a Virgil auth token from the `backend` and registers with Virgil, which generates their private and public key. The app stores the private key locally, and the public key in Virgil Cloud.
* Once the user decides who they want to chat with, the app creates and joins a [Stream Chat Channel](https://getstream.io/chat/docs/#initialize_channel).
* The app asks Virgil's API, via E3Kit, for the receiver's public key.
* The user types a message and encrypts it with the receiver's public key using E3Kit, then sends it to Stream.  After that, Stream Chat relays the message to the receiver. Stream only receives ciphertext, meaning they will never see the original message.
* When the message is received, the app decrypts and verifies it is using E3Kit and passes it along to Stream Chat's iOS components for display.

While this looks complicated, Stream and Virgil do most of the work for us. We'll use Stream's out-of-the-box UI components to render the chat UI and Virgil to do all of the cryptography and key management. We simply combine these services. 

The code is split between the iOS frontend contained in the `ios` folder, and the Express (Node.js) backend is found in the `backend` folder. See the `README.md` in each folder to see installing and running instructions. If you'd like to follow along with running code, make sure you get both the `backend` and `ios` running before continuing.

Let's walk through and look at the critical code needed for each step.

## Prerequisites

Basic knowledge of iOS (Swift) and Node.js is expected. This code is intended to run locally on your machine. 

You will need an account with [Stream](https://getstream.io/accounts/signup/) and [Virgil](https://dashboard.virgilsecurity.com/signup). Once you've created your accounts, you can place your credentials in `backend/.env` if you'd like to run the code. You can use `backend/.env.example` as a reference for the required credentials.  Please see the README in the `backend` directory for more information.

## Step 0. Set up the Backend

For our Swift app to securely interact with Stream and Virgil, the `backend` provides four endpoints:

* `POST /v1/authenticate`: This endpoint generates an auth token that allows the iOS application to communicate with the other endpoints. To keep things simple, this endpoint enables the client to be any user. The frontend tells the backend who it wants to authenticate as. In your application, this should be replaced with real authentication appropriate for your app.

* `POST /v1/stream-credentials`: This returns the data required for the iOS app to establish a session with Stream. In order return this info we need to tell Stream this user exists and ask them to create a valid auth token:
  
{% gist https://gist.github.com/nparsons08/d0d60d997b8dde572a9cbc1db6387eb9 %}
  
The response payload has this shape:
  
{% gist https://gist.github.com/nparsons08/6a1cad88f1fc1504c2ee070c5876c06d %}
  
   * `apiKey` is the Stream account identifier for your Stream instance. It is needed to identify what account your frontend is trying to connect with.
   * `token` JWT token to authorize the frontend with Stream.
   * `user`: This object contains the data that the frontend needs to connect and render the user's view.

* `POST /v1/virgil-credentials`: This returns the authentication token used to connect the frontend to Virgil. We use the Virgil Crypto SDK to generate a valid auth token for us:

{% gist https://gist.github.com/nparsons08/76eb9adfa89a400626834559a7495e2e %}
  
In this case, the frontend only needs the auth token.

* `GET /v1/users`: Endpoint for returning all users, which exists just to get a list of people to chat with. Please refer to the source if you're curious. Please note the `backend` uses in-memory storage, so if you restart, it will forget all of the users.

## Step 1. User Authenticates With Backend

The first step is to authenticate a user and get our Stream and Virgil credentials. To keep thing simple, we have an insecure form that allows you to log in as anyone:

![Image shows a form with a single username field and a login button](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/27480b616641aba4791fe00c4253abc0/login.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=946&wpsize=2048x2048)

This is a simple form that takes any arbitrary name, effectively allowing us to log in as anyone (please use an appropriate authentication method for your application). First, let's add to `Main.storyboard`. We add a "Login View Controller" scene that's backed by a custom controller `LoginViewController` (to be defined). This controller is embedded in a Navigation Controller. Your storyboard should look something like this:

![Image shows a storyboard with a navigation controller pointing to a simple form controller](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/22dc265aa6119394b173db55ad59b435/login-storyboard.png?auto=compress%2Cformat&fit=scale&h=1193&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

The form is a simple `Stack View` with a `username` field and a submit button. Let's look at our custom `LoginViewController`:

{% gist https://gist.github.com/nparsons08/cf4d32527ad1dfe46cc47c244c86a422 %}

The `usernameField` is bound to the storyboard's `Username Field`, and the login method is bound to the `Login` button. When a user clicks login, we check if there's a username and if so, we log in via `Account.shared.login`. `Account` is a shared object that will store our credentials for future backend interactions. Once the user logs in, we initiate the `UsersSegue`, which boots our next scene. We'll see how this is done in a second, but first, let's see how the `Account` object logs in.

Here's how we define `Account`:

{% gist https://gist.github.com/nparsons08/9bf8570c385bd414e92884b4bb80c2d2 %}

First, we set up our shared object that will store our login state in the `authToken` and `userId` properties. Note, `apiRoot`  is the IP address where our backend is running. Please follow the instructions in the `backend` to run it. Our `login` method uses Alamofire (`AF`) to make a `post` request to our backend with the user to log in. Upon success, we store the `authToken` and `userId`, and then we call `setupStream`. 

The method `setupStream` initializes our Stream Chat client. Here's the implementation:

{% gist https://gist.github.com/nparsons08/1415ae96cca7a7f1d109fb9946ecb454 %}

We call to the `backend` with our credentials from `login`. We get back a `token`, which is a Stream Chat token. This token allows our mobile application to communicate directly with Stream without going through our `backend`. We also get an `apiKey`, which identifies the Stream account we're using. We need this data to initialize our Stream `Client` instance and set the user. Last, we initialize Virgil via `setupVirgil`:

{% gist https://gist.github.com/nparsons08/2e6118194ed3ddd027d55c2f65785fda %}

This method requests our Virgil credentials and configures a `VirgilClient` class we defined, which wraps Virgil's E3Kit library. Once that's done, we call the `completion` to indicate success and allow the application to move on.

Let's see what the beginning of our `VirgilClient` implementation looks like:

{% gist https://gist.github.com/nparsons08/ef0a82fb4603b852a80672573e629234 %}

`VirgilClient` is a singleton that is set up via `configure`. We take the `identity` (which is our username) and token, and then we generate a `tokenCallback`. The `EThree` client uses this callback to get a new JWT token. In our case, we've kept it simple by just returning the same token, but in a real application, you'd likely want to replace this with the rest call to the backend.

We use this token callback and identity to initialize `eThree`. We then use this instance to register the user. 

Now we're set up to start chatting!

## Step 2: Listing Users

Next, we'll create a view to list users. In the `Main.storyboard` we add a `UITableView` and back it by our custom `UsersViewController`:

![Image shows a storyboard with a navigation controller with a segue to the login form and a segue to the users controller](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/1a6ddd64c7560b3459c87cf14b6d26eb/users-storyboard.png?auto=compress%2Cformat&fit=scale&h=1089&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

And here's the first few lines of `UsersViewController`:

{% gist https://gist.github.com/nparsons08/902c10d55af71be1dc6848727c38f9fa %}

First, we fetch the users when the view loads. We do this via the `Account` instance configured during login. This action simply hits the `/v1/users` endpoint. Refer to the source if you're curious. We store the users in a `users` property and reload the table view. 

Let's see how we configure the table cells:

{% gist https://gist.github.com/nparsons08/39aca2c4a7a081263cc3b846477f82ab %}

To render the table correctly we indicate the number of rows via `users.count` and set the text of the cell to the user at that index. With this list of users, we can set up a click action on a user's row to start a private 1-on-1 chat with them.

## Step 3: Starting an Encrypted Chat Channel

First, add a new blank view to `Main.storyboard` backed by a new custom class `EncryptedChatViewController` (we'll see its implementation in a minute):

![Image shows the same storyboard, now with an empty encrypted chat controller](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/42317993ca95b0b435b376715aed6964/chat-storyboard.png?auto=compress%2Cformat&fit=scale&h=1006&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

Add a segue between from the user's table row to show the new controller:

![Image shows the same storyboard, now highlighting the segue between the users and encrypted chat controller](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/7801e2120700b244499e24a7e14b2828/segue.png?auto=compress%2Cformat&fit=scale&h=1337&ixlib=php-1.2.1&w=2048&wpsize=2048x2048)

With that setup, we can hook into the segue via the `UITableViewController`'s `prepare` method:

{% gist https://gist.github.com/nparsons08/a565bffaf95aa02cedcae467a3fef269 %}

Before transitioning to the new view, we need to set the view controller up. We generate a unique channel id using the user ids. We initialize a `ChannelPresenter` from the Stream library, set the type to messaging, and restrict the users. We grab the view controller from the segue and back it with the `ChannelPresenter`. This will tell the controller which channel to use. We also tell it what users are communicating.

Let's see how the controller creates this view:

{% gist https://gist.github.com/nparsons08/4fc0ba858b8aac72d406ffd629ba3e6e %}

Luckily, Stream comes with excellent UI components out of the box. We'll simply inherit from `ChatViewController` to do the hard work of displaying a chat. The presenter we set up tells the Stream UI component how to render. All we need to do now is hook into the message lifecycle to encrypt and decrypt messages on the fly.

## Step 4: Sending an Encrypted Message

Now we're ready to send our first encrypted message. Since we're using Stream's built-in UI, all we need to do is hook into the message sending cycle. We'll do this via the `messagePreparationCallback` on the `ChannelPresenter`:

{% gist https://gist.github.com/nparsons08/c9b060ba285ed71d1d700a44c216c7c8 %}

Upon loading the view, we set up the callback, which is a hook provided by Stream, to make any modifications we'd like to the message before sending it over the wire. Since we want to encrypt the message fully, we grab it and modify the text with the `VirgilClient` object. 

For this to work, we need to look up the public key of the other user. Since this action requires a call to the Virgil API, it's asynchronous. We don't want to do this during message preparation, so we do it ahead of time via `VirgilClient.shared.prepareUser(otherUser!)`. Let's see how that method is implemented:

{% gist https://gist.github.com/nparsons08/c06840cf86b8819438d44e4aa33872a7 %}

This is relatively simple with Virgil's E3Kit. We find the user's `Card` which stores all of the information we need to encrypt and decrypt messages. We'll use this `Card` in the `encrypt` method during message preparation:

{% gist https://gist.github.com/nparsons08/fb0cce8ab01e6fc1e9ca23dc951459b6 %}

Once again, this is made easy by Virgil. We simply pass the text and the correct user card to `authEncrypt`, and we're done! Our message is now ciphertext ready to go over the wire. Stream's library will take care of the rest.

## Step #5: Decrypting a Message

Since we're using the `ChatViewController` to render the view, we only need to hook into the cell rendering. We need to decrypt the message text and pass it along. We override the `messageCell` method:

{% gist https://gist.github.com/nparsons08/0397328db98fb7ae086d6a9e2f9bb6f0 %}

We make a copy of the message and set the message's text to the decrypted value. In this case, we need to know if it's our message or theirs. First, we'll look at how to decrypt ours:

{% gist https://gist.github.com/nparsons08/fc57023ae35d51c20f6f204888bd50f9 %}

Since the message was encrypted initially on the device, we have everything we need. We simply ask Virgil E3Kit to decrypt it via `authDecrypt`. Decrypting the other user's messages is a bit more work:

{% gist https://gist.github.com/nparsons08/f7a7ceb2734580977369c24abdc9a70c %}

In this case, we use the same method, but we pass the user card that we retrieved earlier, which verifies the message's authenticity. Now we can see our full chat:

![Image shows an encrypted conversation between two users](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/d0cf368a8c52f14439c5a05865c80189/chat.png?auto=compress%2Cformat&fit=scale&h=2048&ixlib=php-1.2.1&w=946&wpsize=2048x2048)

And that's all! We now have an application that uses end-to-end encryption to protect a user's conversation. Happy coding!