Encryption Tutorial

Introduction

Ably’s encryption feature enables you to send encrypted messages on channels that can later be retrieved and decrypted using a common cryptographic key known only to the client and server. This has the advantage that Ably demonstrably has no access to the un-encrypted contents of your messages, but also means that each app is responsible for enabling the distribution of keys to clients independently of Ably.

Using our encryption API is easy. Let’s get started.

Step 1 – Set up a free account with Ably

In order to run these tutorials locally, you will need an Ably API key. If you are not already signed up, you should sign up now for a free Ably account. Once you have an Ably account:

  1. Log into your app dashboard
  2. Under “Your apps”, click on “Manage app” for any app you wish to use for this tutorial, or create a new one with the “Create New App” button
  3. Click on the “API Keys” tab
  4. Copy the secret “API Key” value from your Root key and store it so that you can use it later in this tutorial

    Copy API Key screenshot

Step 2 – Install Ably

To start using Ably in your JRE application, you need to include the Ably Client library. We recommend that you include the latest client library via Gradle in your project’s gradle.build file.

apply plugin: 'application'
mainClassName = 'io.ably.tutorial.Example'

repositories {
  jcenter()
}

dependencies {
    compile 'io.ably:ably-java:1.0.0'
}

In the above example a specific version of the library is referenced, however we recommend you check which is the latest stable version and always use that. Follow link to get the latest stable release for Java.

Now that you have your API key, let’s define a constant API_KEY in Constants.java.

Remember to replace 'INSERT-YOUR-API-KEY-HERE' with your Ably API key from Step #1.

    class Constants {
    static final String API_KEY = "INSERT-YOUR-API-KEY-HERE"; 
    /* Sign up at ably.io to get your API key */

    /* RuntimeException will be thrown if API_KEY is not set to a proper one */
    static {
    if (Constants.API_KEY.contains("INSERT")) {
        throw new RuntimeException("API key is not set, sign up on ably.io to get yours");
    }
 }
}

Create another class Publisher and initialize AblyRealtime with your API_KEY as follows:

import static io.ably.tutorial.Constants.API_KEY;
import io.ably.lib.realtime.AblyRealtime;
import io.ably.lib.types.AblyException;

public class Publisher {
    public static void main(String[] args) throws AblyException {
        AblyRealtime ablyRealtime = new AblyRealtime(API_KEY);

    }
}

See this step in Github

Let’s create a publisher.html with a input for message to be published and a button to execute the publish action, as shown below:

<html lang="en">
  <body>
    <h1>Publish Encrypted Messages</h1><br><br>
    <label for="message">Message</label>
    <input type="text" id="message">
    <button id="publish">Publish</button>
  </body>
</html>

To start using Ably in your web app, you first need to include the Ably library.
We recommend that you include the latest client library from our CDN using a simple <script> tag.
The client library must be instanced with the API key you copied in Step 1.
Note in production we recommend you always use the token authentication scheme for browser clients, however in this example we use an API key for simplicity.

Include the code below just before your closing your HTML </html> tag.

<script src="https://cdn.ably.io/lib/ably.min-1.js"></script>

<script type="text/javascript">
  var realtime = new Ably.Realtime('INSERT-YOUR-API-KEY-HERE');
</script>
What have we setup in publisher.html so far?
  1. We have added a text box to accept message input from user.
  2. We have provided a button to encrypt and publish the input message. We’ll see how in the following step.
  3. We added Ably library and configured it with our API key. Remember to replace 'INSERT-YOUR-API-KEY-HERE' with your Ably API key from Step #1
See this step in Github

The Ably Realtime and REST libraries are available as an NPM module.
Simply add it to the package.json file of your NodeJs project.

{
    ...
    "dependencies": {
        "ably": ">=1.1"
        ...
    }
}

and run:

npm install

or install it using npm install:

npm install ably

The client library must be instanced with the API key you copied in Step 1. API keys used with basic authentication for your own servers is generally preferred, however, clients running on insecure devices should always use the token authentication scheme instead. In this example, we use an API key for simplicity.

Add the following to constants.js to define constants we’ll use throughout our project:

const API_KEY = 'INSERT-YOUR-API-KEY-HERE'; /* Add your API key here */
if (API_KEY.indexOf('INSERT') === 0) {
    throw('Cannot run without an API key. Add your key to this file.');
}

module.exports = {
    API_KEY: API_KEY
};

Here we defined just one constant API_KEY. Remember to replace 'INSERT-YOUR-API-KEY-HERE' with your Ably API key from Step #1

Now, let’s create a publisher.js with the following content:

const Ably = require('ably');
const constants = require('./constants');

/* Instance the Ably library */
const realtime = new Ably.Realtime({key: constants.API_KEY});

Here, we’re simply importing Ably and constants.js.
Then we define a Realtime instance, which we’ll use to obtain a channel in the next steps.

See this step in Github

Step 3 – Publish encrypted messages to a channel

Let’s see how to leverage the encryption API to publish encrypted messages to a channel.

For that purpose, let’s define a few constants in Constants.java class:
1) CIPHER_KEY_BASE64: the base 64 key we’ll use to encrypt the messages with
2) CHANNEL_NAME: name of the channel we’ll publish the messages to

static final String CIPHER_KEY_BASE64 = "G98udOf2gFPJd0ITsIng8DdQJ32yhAjtRdTsMnCqkmw=";
static final String CHANNEL_NAME = "encrypted:messages";

Note: A sample cipher key is provided here for simplicity.
Alternatively, one could use the io.ably.lib.util.Crypto.generateRandomKey() utility method to generate a valid key.

Now, let’s have a look at how to publish encrypted messages to a channel in the Publisher.java class

public class Publisher {
  public static void main(String[] args) throws AblyException {
    AblyRealtime ablyRealtime = new AblyRealtime(API_KEY);
    ChannelOptions options = ChannelOptions.fromCipherKey(CIPHER_KEY_BASE64);
    Channel channel = ablyRealtime.channels.get(CHANNEL_NAME, options);

    channel.publish(CHANNEL_NAME, "encrypted secret payload");
  }
}

What is going on in the code block above?

  1. We create a ChannelOptions object that specifies the cipher key that we intend to use for the encryption. Note that the cipher key should not be a pass-phrase, but a cryptographic key – generated from a secure random source, 128 or 256 bits long, binary or base64-encoded. If you wish to encrypt messages with a pass-phrase (for example, one entered by a user), you should use a key derivation function to transform that into a key. Here we could also use an Ably utility (Crypto.generateRandomKey()) to generate a random key.
  2. We then obtain a Channel instance for publishing messages. Notice how we pass the ChannelOptions object. This helps us configure the Channel instance further to encrypt the messages before they are published.
  3. And lastly, we publish a message to the CHANNEL_NAME channel by simply calling the publish() method.
  4. In the next step we’ll see how easy it is to retrieve and decrypt this encrypted message.

Let’s update our build.gradle file to apply java plugin and add a gradle task publish so that we can execute Publisher from commandline:

apply plugin: 'java' 
...
task(publish, dependsOn: 'classes', type: JavaExec) {
    main = 'io.ably.tutorial.Publisher'
    classpath = sourceSets.main.runtimeClasspath
}

See this step in Github

Let’s create a publisher in the publisher.html file’s script as follows:

var channelOpts = {
  cipher: {
    key: "AAECAwQFBgcICQoLDA0ODw=="
  }
};

/* Create a publisher with channel options */
var channelName = 'encrypted:messages';
var channelPub = realtime.channels.get(channelName, channelOpts);

/* Publish a message with random number when the publish button is clicked */
var btnPublish = document.getElementById('publish');
btnPublish.addEventListener('click', function () {
  var message = document.getElementById('message').value;
  channelPub.publish(channelName, message);
});

What is going on in the code block above?

  1. We define channelOpts object that specifies the cipher key that we intend to use for the encryption. Note that the cipher key should not be a pass-phrase, but a cryptographic key – generated from a secure random source, 128 or 256 bits long, binary or base64-encoded. If you wish to encrypt messages with a pass-phrase (for example, one entered by a user), you should use a key derivation function to transform that into a key. A sample cipher key is provided here for simplicity. Alternatively, you may use an Ably utility (Ably.Realtime.Crypto.generateRandomKey()) to generate a random key.
  2. We then obtain a publisher (channelPub) instance for publishing messages. Notice how we pass the channelOpts object to realtime.channels.get(). This configures channelPub to encrypt the messages before they are published.
  3. And lastly, we add a click-listener to the button with id publish to publish the input message to the 'encrypted:messages' channel.
  4. In the next step we’ll see how easy it is to retrieve and decrypt this message

See this step in Github

To encrypt our messages, we’ll need a cipher key (SECRET). Let’s add one to our constants.js:

const SECRET = "AAECAwQFBgcICQoLDA0ODw==";

module.exports = {
    API_KEY: API_KEY,
    SECRET: SECRET
};

Note that the cipher key should not be a pass-phrase, but a cryptographic key – generated from a secure random source, 128 or 256 bits long, binary or base64-encoded. If you wish to encrypt messages with a pass-phrase (for example, one entered by a user), you should use a key derivation function to transform that into a key.
A sample cipher key is provided here for simplicity. Alternatively, you may use an Ably utility (Ably.Realtime.Crypto.generateRandomKey()) to generate a random key.

Let’s now see how we would actually encrypt and publish messages to a channel.

const channelOpts = {cipher: {key: constants.SECRET}};
const pubChannel = realtime.channels.get('encrypted:messages', channelOpts);

console.log('Publishing encrypted message');

/* Publish encrypted messages */
pubChannel.publish('unencrypted', 'an encrypted message');

What is going on in the code block above?

  1. We create channelOpts constant that holds the cipher key that we intend to use for the encryption.
  2. We then define (pubChannel) for publishing messages. Notice how we pass the channelOpts variable to realtime.channels.get(). This configures pubChannel to encrypt each message with the cipher key, before it is published.
  3. In the next step, we’ll see how easy it is to retrieve and decrypt encrypted messages.

Let’s add an npm script to our package.json to be able to run our publisher code from the terminal:

...
"scripts": {
    "app:publish": "node ./publisher.js"
},
...

See this step in Github

Step 4 – Subscribe and decrypt messages

Now that we’ve seen how to publish an encrypted message on a channel, let’s see to retrieve it in Subscriber.java class.

public class Subscriber {
  public static void main(String[] args) throws AblyException {
    AblyRealtime ablyRealtime = new AblyRealtime(API_KEY);
    ChannelOptions options = ChannelOptions.fromCipherKey(CIPHER_KEY_BASE64);
    Channel channel = ablyRealtime.channels.get(CHANNEL_NAME, options);

    channel.subscribe(CHANNEL_NAME, message -> System.out.println("Decrypted data: " + message.data));
  }
}

What is going on in the code block above?

  1. We then obtain a Channel instance for subscribing to messages. Notice how we pass the ChannelOptions object the same way we did in the previous step.
  2. By calling subscribe() on the channel, we are all set to receive messages published to the encrypted:messages" channel. Note since we specified the cipher key in @ChannelOptions, that the decryption simply works without any additional setup.
  3. The lambda passed to subscribe accepts a message object. The data field on message holds the decrypted content, which we’re printing out on the standard output.

See this step in Github

Now that we have seen how to publish an encrypted message to a channel, let’s see how we can retrieve it.
Let’s create a subscriber.html with a textarea to display received messages, as shown below:

<html lang="en">
<body>
<h1>Subscribe (and Decrypt) Messages</h1>
<br><br>
<label for="subscriptions">Your subscriptions will appear here:</label>
<textarea id="subscriptions" style="width: 100%" rows="15"></textarea>
</body>

<script src="https://cdn.ably.io/lib/ably.min-1.js"></script>

<script type="text/javascript">
    var realtime = new Ably.Realtime('INSERT-YOUR-API-KEY-HERE');
    var channelName = 'encrypted:messages';

    /* Create options object with cipher(encryption) configuration */
    /* Note: A sample cipher key is provided here for simplicity. Alternatively, one could use
    the Ably.Realtime.Crypto.generateRandomKey() utility method to generate a valid key */
    var channelOpts = {
        cipher: {
            key: "AAECAwQFBgcICQoLDA0ODw=="
        }
    };

    /* Create a subscriber with channel options */
    var channelSub = realtime.channels.get(channelName, channelOpts);

    var textAreaSubscriptions = document.getElementById('subscriptions');
    /* Subscribe to messages from an encrypted channel */
    channelSub.subscribe(function (message) {
        var timestamp = new Date(message.timestamp).toLocaleString();
        textAreaSubscriptions.value += '[' + timestamp + '] ' + message.data + '\n';
    });
</script>
</html>

What is going on in the code block above?

  1. We get a reference to the subscriptions textbox and initialize it’s value to ‘Retrieving encrypted messages from channel…’
  2. We then obtain a channelSub instance. You may have already noticed that it instanced the same way we instanced our publisher in Step 3.
  3. All we’re doing here is calling the channelSub.subscribe() and passing it a callback function that appends each received message to the textbox’s value. We also append a human readable timestamp to each message to tell similar message received at different instants apart.
  4. Notice, how easily we enabled the decryption mechanism simply by using a channelOpts containing our cipher key.

See this step in Github

Now that we have seen how to publish an encrypted message to a channel, let’s see how we can retrieve it.
For this purpose, let’s create a subscriber.js, with following :

const Ably = require('ably');
const constants = require('./constants');

/* Instance the Ably library */
const realtime = new Ably.Realtime({key: constants.API_KEY});
const channelOpts = {cipher: {key: constants.SECRET}};
const subChannel = realtime.channels.get('encrypted:messages', channelOpts);

console.log('Retrieving encrypted message');

/* Subscribe to messages */
subChannel.subscribe(console.log);

What is going on in the code block above?

  1. We obtain a subChannel instance very similar to how we did for pubChannel.
  2. The only difference being, in this instance we’ll subscribe to the channel instead of publishing to it, using the Channel.subscribe()
  3. For simplicity, we’re passing a console.log to the subscribe() to log each received message.
  4. In a real-world project, you would do more than just log the incoming message, and can define a more appropriate callback per your requirements.
  5. But notice how easily we enabled the decryption mechanism, merely by specifying a channelOpts containing our cipher key, during our channel instantiation.

Let’s add one more npm script to our package.json to be able to run our subscriber code from the terminal:

"scripts": {
    "app:publish": "node ./publisher.js",
    "app:subscribe": "node ./subscriber.js"
},

See this step in Github

We’re done, it’s that simple. We have now shown you how to install Ably, then we published an encrypted message on a channel, and later retrieved it using the encryption API.

Live demo

In this demo, we use the encryption API to publish and retrieve encrypted messages. Open this demo in a new browser window to see publishing & subscribing of encrypted messages in action.

Message:

Download tutorial source code

The complete source code for each step of this tutorial is available on Github.

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout encryption-java

To run the demo locally, Make sure to add your Ably API key to Constants.java, if you haven’t already done so.
Open 2 terminal windows and cd into the root of your project, e.g. if your project is in /usr/dev/encryption-tutorial

Execute the following to start the subscription in first window

./gradlew task subscribe

Execute the following to publish a message in the second window

./gradlew task publish

Go back to your first terminal window and you should see the message in your terminal.
And there you have it, a fully functional publish-subscribe demo with encryption enabled.

The complete source code for each step of this tutorial is available on Github.

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout encryption-javascript

To run the demo locally:

  1. Make sure to add your Ably API key to publisher.html and subscriber.html files, if you haven’t already done so.
  2. Open both publisher.html and subscriber.html files in separate tabs/windows in your favorite browser.
  3. Enter a message in the input box in the publisher.html window and click on Publish button, and voila! You should see this message appear on the subscriber.html window with a timestamp.

The complete source code for each step of this tutorial is available on Github.

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout encryption-nodejs

To run the demo locally:

Make sure to add your Ably API key to constants.js, if you haven’t already done so.

Open 2 terminal windows and cd into the root of your project, e.g. if your project is in /usr/dev/encryption-tutorial

cd /usr/dev/encryption-tutorial

Execute the following to start the subscription in first window

npm run app:subscribe

Execute the following to publish a message in the second window

npm run app:publish

Go back to your first terminal window and you should see the message in your terminal.
And there you have it, a fully functional publish-subscribe demo with encryption enabled.

Next steps

1. If you would like to find out more about how to use the encryption API from your devices and apps, see the Realtime encryption documentation. Typically on servers customers prefer to use the REST encryption API
2. Learn more about Ably features by stepping through our other Ably tutorials
3. Gain a good technical overview of how the Ably realtime platform works
4. Get in touch if you need help