Encrypting data

As described in How to use the akka-gdpr module, you can take advantage of the provided encryption implementation or plug in your own. The akka-gdpr module also provides options for key management and for testing during development. The sections on this page describe how to add a dependency on akka-gdpr and provide more detail on working with GdprEncryption, AbstractGdprEncryption, JavaKeyStoreGdprEncryption, and TestGdprEncryption.

Note: Implementations of GdprEncryption, AbstractGdprEncryption and KeyManagement must be thread-safe, as they may be accessed by multiple threads concurrently.

GdprEncryption

The GDPR for Akka Persistence module defines a GdprEncryption extension that you need to implement to plug in encryption and key management. The akka.persistence.gdpr.scaladsl.GdprEncryptionakka.persistence.gdpr.javadsl.GdprEncryption extension has three methods to implement:

  • encrypt - Encrypt the given payload with the key identified by dataSubjectId.
  • decrypt - Decrypt the given payload with the key identified by dataSubjectId, unless the key has been removed.
  • shred - Remove the given dataSubjectId.

If you need full control over encryption algorithms you can implement this interface. However, it is expected most implementations will extend akka.persistence.gdpr.scaladsl.AbstractGdprEncryptionakka.persistence.gdpr.javadsl.AbstractGdprEncryption.

AbstractGdprEncryption

The AbstractGdprEncryption implements all of the methods from GdprEncryption but adds a KeyManagement interface, which enables you to plug in logic for creating new keys and retrieving keys.

The KeyManagment interface has three methods to implement:

  • shred - Remove the key identified by the given dataSubjectId permanently.
  • getOrCreateKey - Create a SecretKey if it doesn’t exist already.
  • getKey - Retrieve the key identified by the given dataSubjectId, returning None if the key has been shredded.

An example without actual implementation of the methods may look like this:

Scala
import scala.concurrent.Future
import akka.Done
import akka.actor.ExtendedActorSystem
import akka.persistence.gdpr.scaladsl.AbstractGdprEncryption
import akka.persistence.gdpr.scaladsl.KeyManagement
import javax.crypto.SecretKey

class ExampleGdprEncryption(system: ExtendedActorSystem, configPath: String) extends AbstractGdprEncryption(system) {
  override protected val keyManagement: KeyManagement = {
    val keySize = system.settings.config.getInt("akka.persistence.gdpr.key-size")
    new ExampleKeyManagement(keySize)
  }
}

class ExampleKeyManagement(keySize: Int) extends KeyManagement {

  /**
   * Remove the key identified by dataSubjectId permanently.
   */
  override def shred(dataSubjectId: String): Future[Done] = {
    ??? // FIXME implement this
  }

  /**
   * Create a `SecretKey` if it doesn't exist already. Length of the key should be taken
   * from `akka.persistence.gdpr.key-size`
   *
   * If using `AbstractGdprEncryption` this must be an AES key.
   */
  override def getOrCreateKey(dataSubjectId: String): Future[SecretKey] = {
    ??? // FIXME implement this
  }

  /**
   * Retrieve the key identified by dataSubjectId, returning None if the key has been shredded.
   */
  override def getKey(dataSubjectId: String): Future[Option[SecretKey]] = {
    ??? // FIXME implement this
  }
}
Java
import akka.Done;
import akka.actor.ExtendedActorSystem;
import akka.persistence.gdpr.javadsl.AbstractGdprEncryption;
import akka.persistence.gdpr.javadsl.KeyManagement;

import javax.crypto.SecretKey;
import java.util.Optional;
import java.util.concurrent.CompletionStage;

public class ExampleGdprEncryption extends AbstractGdprEncryption {
  private final KeyManagement keyManagement;

  static class ExampleKeyManagement implements KeyManagement {
    private final int keySize;

    ExampleKeyManagement(int keySize) {
      this.keySize = keySize;
    }

    /**
     * Remove the key identified by dataSubjectId permanently.
     */
    @Override
    public CompletionStage<Done> shred(String dataSubjectId) {
      // FIXME implement this
      throw new RuntimeException("To be implemented");
    }

    /**
     * Create a `SecretKey` if it doesn't exist already. Length of the key should be taken
     * from `akka.persistence.gdpr.key-size`
     *
     * If using `AbstractGdprEncryption` this must be an AES key.
     */
    @Override
    public CompletionStage<SecretKey> getOrCreateKey(String dataSubjectId) {
      // FIXME implement this
      throw new RuntimeException("To be implemented");
    }

    /**
     * Retrieve the key identified by dataSubjectId, returning None if the key has been shredded.
     */
    @Override
    public CompletionStage<Optional<SecretKey>> getKey(String dataSubjectId) {
      // FIXME implement this
      throw new RuntimeException("To be implemented");
    }
  }

  public ExampleGdprEncryption(ExtendedActorSystem system, String configPath) {
    super(system);

    int keySize = system.settings().config().getInt("akka.persistence.gdpr.key-size");
    keyManagement = new ExampleKeyManagement(keySize);

  }

  @Override
  public KeyManagement keyManagement() {
    return keyManagement;
  }
}

JavaKeyStoreGdprEncryption

An existing implementation of GdprEncryption is included in JavaKeyStoreGdprEncryption. It has support for PKCS12 and JCEKS keystores. It can only be used for single node applications since it saves to a local file so won’t be available for other nodes in the cluster. If your application is distributed you’ll need to create a KeyManagement implementation for your distributed secret store e.g. Vault. JavaKeyStoreGdprEncryption can be enabled with configuration:

akka.persistence.gdpr.encryption-provider = "akka.persistence.gdpr.jca-provider"

It’s also important to be aware of that the the JavaKeyStoreGdprEncryption doesn’t have any redundancy, so if the storage is corrupted for some reason all encrypted data is “lost”.

To configure JavaKeyStoreGdprEncryption to use a local PKCS12 or JCEKS key store you need to specify:

  • Location of your keystore file
  • Password for your keystore
  • Key size (keys will be generated for new data subjects)
  • Keystore type
akka.persistence.gdpr.jca-provider {
  class = "akka.persistence.gdpr.JavaKeyStoreGdprEncryption"
  # Possible values: jceks, pkscs12
  keystore-type = "jceks"

  # Set this from env variable. Do not store passwords in configuration.
  keystore-password = "password"

  # Path to the keystore
  keystore-location = ""

  # Java key store API has blocking calls.
  use-dispatcher = "akka.actor.default-blocking-io-dispatcher"
}

It’s recommended to configure the key-size to 256, which may require installation of Java Cryptography Extension (JCE).

akka.persistence.gdpr.key-size = 256

TestGdprEncryption

For unit testing purposes TestGdprEncryption is provided. Don’t use this test module in production, since it is only holding the generated keys in memory. TestGdprEncryption can be can be enabled with the following configuration:

akka.persistence.gdpr.encryption-provider = "akka.persistence.gdpr.test-provider"

Pointing to your encryption implementation

Define the akka.persistence.gdpr.encryption-provider setting to point to a configuration block for your encryption implementation. The only mandatory field in that block is class, which is a fully qualified class name of your implementation of GdprEncryption. The class must have a constructor that takes in an ExtendedActorSystem and the config path as a String.

akka.persistence.gdpr {
  # It is mandatory to define this property. It's path to the configuration
  # section for your encryption mechanism. E.g. "akka.persistence.gdpr.jca-provider".
  # That section must contain a "class" property with a FQCN of an implementation of the
  # GdprEncryption Extension with a constructor that takes in an ExtendedActorSystem
  # and the config path as a String.
  encryption-provider = ""

  # 128, 192 or 256
  key-size = 128

  # Valid values are 128, 120, 112, 104, 96
  # See https://docs.oracle.com/javase/8/docs/api/javax/crypto/spec/GCMParameterSpec.html
  gcm-tlen = 96
}

We recommend configuring the key-size to 256, which may require installation of Java Cryptography Extension (JCE).

akka.persistence.gdpr.key-size = 256