Encryption
Ably client libraries support built-in symmetric encryption of message content, making it easier to build apps that encrypt content fully end-to-end. Whilst TLS is enabled by default and ensures that data is securely sent to and received from Ably, messages are not encrypted within the Ably system. Using the encryption feature of our client libraries ensures that message payloads are opaque, can never be decrypted by Ably, and can only be decrypted by other clients that share your secret key.
Getting started
Channels can be easily configured to automatically encrypt and decrypt all message payloads using the secret key
provided in the channel options. Below is a simple example:
Unfortunately at present it is not possible to setup channel options in Go.
Ably.Rest.Crypto.generateRandomKey(function(err, key) {
var channelOpts = { cipher: { key: key } };
var channel = rest.channels.get('bot-mad-ice', channelOpts);
channel.publish('unencrypted', 'encrypted secret payload');
});
Ably.Rest.Crypto.generateRandomKey(function(err, key) {
var channelOpts = { cipher: { key: key } };
var channel = rest.channels.get('bot-mad-ice', channelOpts);
channel.publish('unencrypted', 'encrypted secret payload');
});
key = Ably::Util::Crypto.generateRandomKey()
channel_opts = { cipher: { key: key } }
channel = rest.channels.get('bot-mad-ice', channel_opts)
channel.publish 'unencrypted', 'encrypted secret payload'
key = ably.util.crypto.generate_random_key()
channel = rest.channels.get('bot-mad-ice', cipher={'key': key})
channel.publish(u'unencrypted', u'encrypted secret payload')
$key = Ably\Utils\Crypto->generateRandomKey();
$channelOpts = array('cipher' => array('key' => $key));
$channel = rest->channels->get('bot-mad-ice', $channelOpts);
$channel->publish('unencrypted', 'encrypted secret payload');
ChannelOptions options = ChannelOptions.withCipherKey(<key>);
Channel channel = rest.channels.get("bot-mad-ice", options);
channel.publish("unencrypted", "encrypted secret payload");
AblyRest rest = new Ably.Rest('xVLyHw.sgVz_w:2CrR6u-8IK4J6l4E');
byte[] key = Crypto.GenerateRandomKey();
ChannelOptions options = new ChannelOptions(key);
IRestChannel channel = rest.Channels.Get("bot-mad-ice", options);
await channel.PublishAsync("unencrypted", "encrypted secret payload");
ARTChannelOptions *options = [[ARTChannelOptions alloc] initWithCipherKey:<key>];
ARTRestChannel *channel = [rest.channels get:@"bot-mad-ice" options:options];
[channel publish:@"unencrypted" data:@"encrypted secret payload"];
let options = ARTChannelOptions(cipherKey: <key>)
let channel = rest.channels.get("bot-mad-ice", options: options)
channel.publish("unencrypted", data: "encrypted secret payload")
Note that the 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. The client libraries are also capable of generating a random key for you.
If you are using Python 2 and you wish to pass in a base64-encoded key, make sure you pass it in as a unicode
string, not a str
, or the library will interpret it as a binary.
Understanding encryption
The libraries support encryption purely as a convenience; the libraries ensure interoperability between environments by having compatible implementations of encryption algorithms and by making common choices on format, mode, padding etc. However, Ably intentionally does not manage the distribution of keys between clients, and end-to-end encryption is enabled without exposing keys to the Ably service at all. 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.
The client library support for encryption supports symmetric encryption only, and requires each participating client to each specify the correct CipherParams
secret key
when creating a Channel
instance. Clients that do not specify a key will be delivered the still-encrypted message payloads that they may then still wish to decrypt offline.
The client libraries are designed to be extensible, but initially only support the AES algorithm (with a default key length of 256 bits) and CBC mode. These defaults are intended to ensure that encryption support can be provided in all target environments and platforms.
Encryption is supported for the data
attribute (payload) of published messages and presence member messages on a channel, over both REST and realtime publish operations. Decryption is supported in realtime message and presence message subscriptions and in REST history, REST presence get, and REST presence history operations.
All other attributes of messages and presence messages, such as event name
or clientId
client_id
ClientId
remain un-encrypted. All sensitive data, when using the library’s symmetric encryption, must be placed in the data
attribute to ensure it is encrypted before it is transmitted to Ably.
The key in use at any given time is known by the client library, but the Ably service has no visibility of the key; it knows only that a given message payload was encrypted. When accessing messages via the history API, it is the caller’s responsibility to ensure that the correct key is configured for the channel before the history request is made.
Encryption options (algorithm, key, etc) are specified on a per-channel basis; it is expected that apps may wish to have both un-encrypted and encrypted channels on a single connection.
Crypto API Reference
- Methods
- Related types
The Ably.Rest.Crypto
Ably::Util::Crypto
Ably\Utils\Crypto
ably.util.crypto
io.ably.lib.util.crypto
ARTCrypto
IO.Ably.Encryption.Crypto
object exposes the following public methods:
Methods
getDefaultParamsget_default_paramsget_default_paramsGetDefaultParamsDefaultCipherParams
CipherParams Crypto.getDefaultParams(Object params)CipherParams Crypto.get_default_params(Hash params)CipherParams Crypto.get_default_params(Dict params)CipherParams Crypto.getDefaultParams(Array params)CipherParams Crypto.getDefaultParams(Param[] params)CipherParams GetDefaultParams(byte[] key = null, byte[] iv = null, CipherMode? mode = null)getDefaultParams(values: [NSObject : AnyObject]) → ARTCipherParamsDefaultCipherParams() (*CipherParams, error)
This call obtains a CipherParams
object using the values passed in (which must be a subset of CipherParams
fields that at a minimum includes a key
), filling in any unspecified fields with default values, and checks that the result is a valid and self-consistent.This call takes a key, an initialization vector (iv) and a Cipher mode. There is also on override which accepts the key
and iv
as base64 encoded strings. It will validate the passed values and generate CipherParams
returns a CipherParams
object with fields set to default values. This generates random secret key and initialization vector (iv) values.
You will rarely need to call this yourself, since the client library will handle it for you if you specify cipher
params when initializing a channel (as in the example at the top) or when setting channel options with channel#setOptions
.
Parameters
- paramsarguments
- The cipher paramsarguments that you want to specify. It must at a minimum include a
key
, which should be either a binary (byte[]
ArrayBuffer
orWordArray
Buffer
byte arrayNSData
) or a base64-encodedNS
String
.
Returns
On success, the method returns a complete CipherParams
object. Failure will raise an AblyException
exception.
Example
var cipherParams = Ably.Rest.Crypto.getDefaultParams({key: <key>});
var channelOpts = { cipher: cipherParams };
var channel = rest.channels.get('bot-mad-ice', channelOpts);
var cipherParams = Ably.Rest.Crypto.getDefaultParams({key: <key>});
var channelOpts = { cipher: cipherParams };
var channel = rest.channels.get('bot-mad-ice', channelOpts);
cipher_params = Ably::Util::Crypto.get_default_params({key: <key>})
channel_opts = { cipher: cipher_params }
channel = rest.channels.get('bot-mad-ice', channel_opts)
cipher_params = ably.util.crypto.get_default_params({'key': <key>})
channel = rest.channels.get('bot-mad-ice', cipher=cipher_params)
$cipherParams = Ably\Utils\Crypto->getDefaultParams(array('key' => <key>));
$channelOpts = array('cipher' => $cipherParams);
$channel = $rest->channels->get('bot-mad-ice', $channelOpts);
CipherParams params = Crypto.getDefaultParams(new Param[]{ new Param("key", <key>) });
ChannelOptions options = new ChannelOptions();
options.encrypted = true;
options.cipherParams = params;
Channel channel = rest.channels.get("bot-mad-ice", options);
CipherParams cipherParams = Crypto.GetDefaultParams(<key>);
var channel = rest.Channels.Get("bot-mad-ice", new ChannelOptions(cipherParams));
ARTCipherParams *params = [ARTCrypto getDefaultParams:@{@"key": <key>}];
ARTChannelOptions *options = [[ARTChannelOptions alloc] initWithCipher:params];
ARTRealtimeChannel *channel = [rest.channels get:@"bot-mad-ice" options:options];
let params = ARTCrypto.getDefaultParams(["key": <key>])
let options = ARTChannelOptions(cipher: params)
let channel = rest.channels.get("bot-mad-ice", options: options)
params, err := Crypto.DefaultCipherParams()
generateRandomKeygenerate_random_keygenerate_random_keyGenerateRandomKey
Crypto.generateRandomKey(Int keyLength?, callback(ErrorInfo err,
Buffer
key))byte array Crypto.generate_random_key(Int key_length?)byte array Crypto.generate_random_key(Int key_length?)string Crypto.generateRandomKey(Int keyLength?)byte[]
Crypto.generateRandomKey(Int keyLength?)byte[] GenerateRandomKey(int? keyLength = null, CipherMode? mode = null)generateRandomKey(length?: UInt) → NSDataGenerateRandomKey(keyLength …int) ([]byte, error)
This call obtains a randomly-generated binary key of the specified key length and optional CipherMode.
Parameters
- keyLengthkey_length
- Optional
Int
with the length of key to generate. For AES, this should be either 128 or 256. If unspecified, defaults to 256.
- mode
- Optional AES
CipherMode
which is used when the key is generated - callback
- is a function of the form
function(err, key)
which is called upon completion
Callback result
On successfully generating a key, the callback is called with that key as a WordArray
Buffer
, and err
is null
. On failure to create a key, err
contains an ErrorInfo
object describing the failure reason.
Returns
On success, the method returns the generated key as a byte[]
arraybytes
byte arraybinary stringNSData
[]byte
array. Failure will raise an AblyException
Failure will cause error to contain an ErrorInfo
object describing the failure reason.
Example
Ably.Rest.Crypto.generateRandomKey(256, function(err, key) {
if(err) {
console.log("Key generation failed: " + err.toString());
} else {
var channel = rest.channels.get('bot-mad-ice', {cipher: {key: key}});
}
});
Ably.Rest.Crypto.generateRandomKey(256, function(err, key) {
if(err) {
console.log("Key generation failed: " + err.toString());
} else {
var channel = rest.channels.get('bot-mad-ice', {cipher: {key: key}});
}
});
key = Ably::Util::Crypto.generate_random_key(256)
channel = rest.channels.get('bot-mad-ice', { cipher: {key: key}})
cipher_params = ably.util.crypto.generate_random_key(256)
channel = rest.channels.get('bot-mad-ice', cipher={'key': key})
$key = Ably\Utils\Crypto->generateRandomKey(256);
$channel = $rest->channels->get('bot-mad-ice', array('cipher' => array('key' => $key)));
byte[] key = Crypto.generateRandomKey(256);
ChannelOptions options = ChannelOptions.withCipher(key);
Channel channel = rest.channels.get("bot-mad-ice", options);
byte[] key = Crypto.GenerateRandomKey(256);
ChannelOptions options = new ChannelOptions(key);
var channel = rest.Channels.Get("bot-mad-ice", options);
NSData *key = [ARTCrypto generateRandomKey:256];
ARTChannelOptions *options = [[ARTChannelOptions alloc] initWithCipherKey:key];
ARTRealtimeChannel *channel = [rest.channels get:@"bot-mad-ice" options:options];
let key = ARTCrypto.generateRandomKey(256)
let options = ARTChannelOptions(cipherWithKey: key)
let channel = rest.channels.get("bot-mad-ice", options: options)
key, err := Crypto.GenerateRandonKey(256)
Related types
ChannelOptions ObjectARTChannelOptionsChannelOptions HashChannelOptions DictChannelOptions ArrayIO.Ably.ChannelOptionsio.ably.lib.types.ChannelOptions
Currently the supported channel options are only used for configuring encryption.
ChannelOptions
, a plain Javascript object, may optionally be specified when instancing a Channel
, and this may be used to specify channel-specific options. The following attributes can be defined on the object:
ChannelOptions
, a Hash object, may optionally be specified when instancing a Channel
, and this may be used to specify channel-specific options. The following key symbol values can be added to the Hash:
ChannelOptions
, an Associative Array, may optionally be specified when instancing a Channel
, and this may be used to specify channel-specific options. The following named keys and values can be added to the Associated Array:
ART
io.ably.lib.types.
ChannelOptions
may optionally be specified when instancing a Channel
, and this may be used to specify channel-specific options.
IO.Ably.ChannelOptions
may optionally be specified when instancing a Channel
, and this may be used to specify channel-specific options.
PropertiesMembersAttributes
- cipher:cipherCipherParams
- Requests encryption for this channel when not null, and specifies encryption-related parameters (such as algorithm, chaining mode, key length and key). See an example
Type:CipherParams
or an options objectaParam[]
listan options hashan Associative Array containing at a minimum akey
Static methods
withCipherKey
static ChannelOptions.withCipherKey(Byte[] or String key)
A helper method to generate a ChannelOptions
for the simple case where you only specify a key.
Parameters
- key
- A binary
Byte[]
array or a base64-encodedString
.
Returns
On success, the method returns a complete ChannelOptions
object. Failure will raise an AblyException
.
CipherParamsARTCipherParamsCipherParams HashCipherParams DictCipherParams ArrayIO.Ably.CipherParamsio.ably.lib.util.Crypto.CipherParams
A CipherParams
contains configuration options for a channel cipher, including algorithm, mode, key length and key. Ably client libraries currently support AES with CBC, PKCS#7 with a default key length of 256 bits. All implementations also support AES128.
Individual client libraries may support either instancing a CipherParams
directly, using Crypto.getDefaultParams()
Crypto.GetDefaultParams()
Crypto.get_default_params()
, or generating one automatically when initializing a channel, as in this example.
PropertiesMembersAttributes
- keyKey:key
- A binary (
byte[]
ArrayBuffer
orWordArray
Buffer
byte arrayNSData
) containing the secret key used for encryption and decryption
- algorithm:algorithmAlgorithm
-
AES The name of the algorithm in the default system provider, or the lower-cased version of it; eg “aes” or “AES”
Type:String
- key_length:key_lengthkeyLengthKeyLength
-
256 The key length in bits of the cipher, either 128 or 256
Type:Integer
- mode:modeMode
-
CBC The cipher mode
Type:String
CipherMode
- keySpec
- A
KeySpec
for the cipher key
Type:SecretKeySpec