End-to-End Encrypted iOS Chat with Apple's CryptoKit
Tags: ios, security, beginners, tutorial
Published: 2020-11-06 18:05:34

---
title: End-to-End Encrypted iOS Chat with Apple's CryptoKit
published: true
description: 
tags: ios, security, beginners, tutorial
canonical_url: https://getstream.io/blog/ios-cryptokit-framework-chat/
---

In most cases, when building a chat app, it's essential to provide adequate privacy and security to your users. This can be done using cryptographic methods such as end-to-end encryption. End-to-end encryption is becoming a mainstream expectation, as it's featured in the biggest chat apps, such as WhatsApp and Telegram.

![Image shows two chat screens, one with messages encrypted and the other with messages decrypted](https://i.imgur.com/7aTLaWG.png)

In this article, you'll learn how basic end-to-end encryption works in an iOS chat app using Apple's own CryptoKit framework for its secure and high-level encryption methods and [Stream's flexible iOS chat SDK](https://getstream.io/tutorials/ios-chat/) for its ready-made chat networking and UI components.

**Please note that this tutorial is very basic and strictly educational, may contain simplifications, and rolling your own encryption protocol is not advisable. The algorithms used can contain certain 'gotchas' if not employed properly with the help of security professionals. Still, reading this article is a great start to get practical knowledge.**

You can find the completed project on GitHub: [encrypted-ios-chat](https://github.com/GetStream/encrypted-ios-chat). And if you have any questions, feel free to reach out to me on Twitter: [@cardosodev](https://twitter.com/cardosodev) :).

## What Is End-to-End Encryption?
> End-to-end encryption is a communication system where the only people who can read the messages are the people communicating. No eavesdropper can access the cryptographic keys needed to decrypt the conversation—not even a company that runs the messaging service.
> - [Hacker Lexicon: What Is End-to-End Encryption](https://www.wired.com/2014/11/hacker-lexicon-end-to-end-encryption/)

## What Is Apple's CryptoKit?
> CryptoKit is a new Swift framework that makes it easier and safer than ever to perform cryptographic operations, whether you simply need to compute a hash or are implementing a more advanced authentication protocol.
> - [WWDC19: Cryptography and Your Apps](https://developer.apple.com/videos/play/wwdc2019/709/)
> - [Hacker Lexicon: What Is End-to-End Encryption](https://www.wired.com/2014/11/hacker-lexicon-end-to-end-encryption/)

## What You Need
- Xcode 11 or later
- iOS 13 or later
- A [Stream](https://getstream.io/accounts/signup/) account

## Basic Encryption Methods

Before we get to the chat part, let's start by defining the basic encryption methods we need. We'll integrate these methods in the next section.

### 1. Generating a Private Key

A **private key** is essential to end-to-end encryption. It is used alongside a **public key** to generate a **symmetric key** which is used for **encryption** and **decryption** of data. Each user in your application has a private and public key. They share their public keys with each other so that they can generate the same symmetric key. Alice shares her public key with Bob, and Bob uses his private key and Alice's public key to generate a symmetric key. Conversely, Bob shares his public key with Alice, and Alice uses her private key with Bob's public key to generate the same symmetric key. As long as Alice's and Bob's private keys are a secret to each of them, no one can generate the same symmetric key to decrypt their conversations.

To generate a private key, we'll use the `P256.KeyAgreement.PrivateKey()` initializer.

{% gist https://gist.github.com/cardoso/ef1367f8ffe790c1b96a5baf7cb9cead %}

Additionally, I chose the P256 algorithm for its cross-platform availability, as it is supported by the [Web Crypto API](https://www.w3.org/TR/WebCryptoAPI/#dfn-NamedCurve-p256) in case you need a web version. Also, it's a good balance between performance and security. This preference can change with time as new algorithms become available.

PS: The public key can be extracted from the private key using `privateKey.publicKey`. We'll touch on this in the integration part of this article.

The private key must be saved somewhere safe. In order to do this, you may need to turn it into a string format to save it and back to perform the cryptographic operations. You can do this with the following `exportPrivateKey` method:

{% gist https://gist.github.com/cardoso/78d7e63f3975a8a9f21b1b8629736e81 %}

And the `importPrivateKey` method:

{% gist https://gist.github.com/cardoso/e333ba6fbfb57e7fac83296c9a77e15e %}

### 2. Deriving Symmetric Key

Once a user (message sender) has the public key of the other user it wants to communicate with (message recipient), that user can generate the symmetric key using the methods: `privateKey.sharedSecretFromKeyAgreement(with: publicKey)` and `sharedSecret.hkdfDerivedSymmetricKey`.

{% gist https://gist.github.com/cardoso/c9e381b78671263cfc2e65d568f77bdc %}

This process is called a [Diffie-Hellman Key Exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) and ensures the symmetric key can be shared between two users without ever leaving their devices.

### 3. Encrypt Text

Now, we use that symmetric key for encrypting texts using the `AES.GCM.seal` method. We also encode it in a Base64 string, so it can be sent as normal text using the [Stream Chat](https://getstream.io/chat/) SDK in the integration step.

{% gist https://gist.github.com/cardoso/5f7406767e9d18d82b7b153f12bb9b0e %}

After the text is encrypted and encoded, it can be safely sent through the network.

### 4. Decrypt Text

After the recipient receives the encrypted text, they will decode it from Base64 and decrypt it using the `AES.GCM.SealedBox` and `AES.GCM.open` methods.

{% gist https://gist.github.com/cardoso/d269aa10e6085993beb922213b7cbaf8 %}

After the text is decrypted, it should be readable and can be displayed in the UI.

## Stream Chat Integration

In this section, you'll learn how to use the methods implemented above to achieve end-to-end encryption with [Stream Chat's Swift SDK](https://github.com/getstream/stream-chat-swift). 

![Image shows the stream chat swift SDK banner image](https://i.imgur.com/vLa7014.png)

You can follow the "iOS Chat SDK Setup" step in the [official tutorial](https://getstream.io/tutorials/ios-chat) to get a basic chat app working in minutes.

### 1. Public Key Extra Data

We need to define a custom `publicKey` field for the User object so that users can share their public key with others. To do this, create the `PublicKeyExtraData` type.

{% gist https://gist.github.com/cardoso/a2b219c5adeda4de1ed08c3ee86de7a2 %}

After that, you should set the `User.extraDataType` to `PublicKeyExtraData.self` before you initialize the Stream Chat Client in `AppDelegate.swift`:

{% gist https://gist.github.com/cardoso/f39c7e7ff0280ce208d270f40d88848d %}

### 2. Custom Set User Method

Now, let's define our custom `setUser` method so that the user can be registered in Stream Chat alongside its public key.

{% gist https://gist.github.com/cardoso/202e3985cd6ed882c4058caf9d5f0914 %}

Additionally, the public key must be exported in a string format. You should define an `exportPublicKey` method to do this.

{% gist https://gist.github.com/cardoso/036fc96ce0461d13af87078d036ce7f0 %}

We're percent-encoding the public key after encoding to base64 to ensure the + sign is not ignored in case the public key is used in the URLs of specific requests.

In a future step, after we fetch another user's public key, we'll need to use the `importPublicKey` method, which does the reverse of the `exportPublicKey`.

{% gist https://gist.github.com/cardoso/d309ea2ea465899fb3e48b988a6b0961 %}

### 3. Encrypted Chat View Controller

Now that we generated the private key and registered our user with a public key, we need a custom `ChatViewController` that encrypts messages before sending and decrypts messages before displaying.

![Animation shows an encrypted chat screen alternating every second between encrypted and decrypted messages](https://i.imgur.com/Pj5atA3.gif)

To do this, let's create the `EncryptedChatViewController`, which inherits from `ChatViewController`.

{% gist https://gist.github.com/cardoso/714cbd474f233675f34b83708448ab46 %}

As you can see, it contains an additional `symmetricKey` property, which will be used for the encryption and decryption. In the `viewDidLoad` we set the `messagePreparationCallback`, which encrypts the messages before they're sent. We also override the `messageCell` method to decrypt the message before displaying it.

### 3. Present Encrypted Chat

After implementing our custom `ChatViewController`, now we can present it.

{% gist https://gist.github.com/cardoso/74e0fa352ba2a47397079a9553e57310 %}

To present the `EncryptedChatViewController`, we query the user we want to communicate with to get its public key and generate a symmetric key. After that, we set the `symmetricKey` and `presenter` properties and push the view controller into the navigation stack.

## Next Steps With CryptoKit

Congratulations! You just learned how to implement basic end-to-end encryption in your iOS chat apps. It's important to know this is the most basic form of end-to-end encryption. It lacks some additional tweaks that can make it more bullet-proof for the real world, such as [randomized padding](https://en.wikipedia.org/wiki/Padding_(cryptography)#Randomized_padding), [digital signature](https://en.wikipedia.org/wiki/Digital_signature), and [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy), among others. Also, for real-world usage, it's vital to get the help of application security professionals.