Encryption

Ebean has support for transparent Encryption/Decryption of specific properties. We make mark the properties we want encrypted with @Encrypted and these properties will be automatically encrypted and decrypted as needed.

Encyption/decryption can occur either on the client/application side or by the database. When we use database encryption then we can use these properties in queries as part of the where and order by clause. Effectively the encryption of the property is fully transparent to the application.

When we use client/application side encyption/decryption then we can't use the property in the where and order by clause.

Client/Application encryption

When using client/application encyrption the properties are encrypted/decrypted by a Java function - an implementation of the io.ebean.config.Encryptor interface. The default implementation uses AES 128 bit based implementation and we can also configure Ebean to use another implementation.

Properties encrytped with client/application encryption can NOT be used in where and order by clause.

package io.ebean.config;

/**
 * Used for Java side encryption of properties when DB encryption is not used.
 *
 * By default this is used on non-varchar types such as Blobs.
 */
public interface Encryptor {

  /**
   * Encrypt the data using the key.
   */
  byte[] encrypt(byte[] data, EncryptKey key);

  /**
   * Decrypt the data using the key.
   */
  byte[] decrypt(byte[] data, EncryptKey key);

  /**
   * Encrypt the formatted string value using a key.
   */
  byte[] encryptString(String formattedValue, EncryptKey key);

  /**
   * Decrypt the data returning a formatted string value using a key.
   */
  String decryptString(byte[] data, EncryptKey key);

}

Database encryption

When using Database side encryption/decryption we use database stored procedures to encrypt and decrypt the properties. For example with Postgres Ebean uses PGP_SYM_ENCRYPT() and PGP_SYN_DECRYPT().

Database encryption functions

The default DB encryption decryption functions used for each plaform are:

  • Postgres - PGP_SYM_ENCRYPT(), PGP_SYN_DECRYPT()
  • MySql - AES_ENCRYPT(), AES_DECRYPT()
  • Oracle: requires dbms_crypto and uses custom functions for encryption and decryption
  • H2 - ENCRYPT() and DECRYPT() with 'AES' option

Supported types

The following are the types supported by database encryption. Any type not supported by database encryption will use client/application encryption.

  • Enum
  • String
  • byte[]
  • Date types - LocalDate, Date, Joda LocalDate
  • Timestamp types - Timestamp, Instance, OffsetDateTime, ZonedDateTime

EncryptKeyManager

Whenever a property is encrypted or decrypted a "Key" must be used. Ebean will internally ask the EncryptKeyManager for a key given the table and column name.

We must supply an implementation of the EncryptKeyManager.

package io.ebean.config;

/**
 * Determine keys used for encryption and decryption.
 */
@FunctionalInterface
public interface EncryptKeyManager {

  /**
   * Initialise the EncryptKeyManager.
   *
   * This gives the EncryptKeyManager the opportunity to get keys etc.
   */
  default void initialise() {}

  /**
   * Return the key used to encrypt and decrypt a property mapping to the given
   * table and column.
   */
  EncryptKey getEncryptKey(String tableName, String columnName);
}

@Encrypted

Mark a property to be encrypted with the @Encrypted annotation. By default the property will be dbEncryption = true and we explicitly set that false for client/application side encryption.

// use database side encryption
@Encrypted
String name;

// use client side encryption (not db functions)
@Encrypted(dbEncryption=false)
String description;

Example

// Use @Encrypted annotation to mark the encrypted properties

@Entity
@Table(name="patient")
public class Patient {

  @Id
  long id;

  // database side encryption
  @Encrypted
  String name;

  // client side encryption
  @Lob
  @Encrypted(dbEncryption=false)
  String description;

  @Encrypted
  LocalDate dob;
  ...

Limitations

  • Properties using Java client encryption can NOT be used in WHERE clauses
  • Only DB Encryption support built in for H2, Postgres, MySql and Oracle.
  • You can not use Encryption with positioned (1,2,3...) parameters. You must use named parameters or the criteria api to define queries.

Examples:

List<Patient> list =
  new QPatient()
    .name.eq("Rob")
    .findList();

Results in the following Postgres SQL:

select t0.id, pgp_sym_decrypt(t0.name,?)
from patient t0
where pgp_sym_decrypt(t0.name,?) = ?

Configuration

Specify the EncryptKeyManager implementation in the ebean.properties file like below:

ebean.encryptKeyManager=org.example.BasicEncyptKeyManager

Programmatically configure using DatabaseConfig.

DatabaseConfig config = DatabaseConfig();
...
EncryptKeyManager keyManager = ...;
config.setEncryptKeyManager(keyManager);
...
Database database = DatabaseFactory.create(config);

EncryptKeyManager

An example EncryptKeyManager is:

package org.example.encrypt;

import io.ebean.config.EncryptKey;
import io.ebean.config.EncryptKeyManager;

public class BasicEncyptKeyManager implements EncryptKeyManager {

  public void initialise() {
    // can load keys or initialise source resources ...
  }

  public EncryptKey getEncryptKey(String tableName, String columnName) {
    // get the key for the given table and column
    String keyValue = ...;
    return new BasicEncryptKey(keyValue);
  }

}

Internals

Ebean is detecting when an encrypted property is being used. It will call the EncryptKeyManager with the table and column of the property to get the encryption key. This key is then added as a bind variable to the prepared statement.

As the key is added as a bind variable into the statement we can not use encryption with 'positioned' parameters because it can effectively change the position of other parameters. We can use named parameters or the criteria api for building queries but we can't use positioned (1,2,3,4..) parameters.