How to encrypt and decrypt data with Hibernate

Introduction

Today, one of my Twitter followers sent me the following StackOverflow question, and, while answering it, I realized that it definitely deserves a post of its own.

In this post, I will explain how you can encrypt and decrypt data with Hibernate.

PostgreSQL crypto module

Because the StackOverflow question mentions PostgreSQL, we first need to enable the pgcrypto extension. For this purpose, we need to execute the following statement:

CREATE EXTENSION pgcrypto;

The pgcrypto will allow us to use the pgp_sym_encrypt and pgp_sym_decrypt functions.

Domain Model

Assuming we have the following entity:

vault

The storage column needs to be encrypted upon being written and decrypted during a read operation.

@ColumnTransformer annotation to the rescue!

Luckily, Hibernate offers the @ColumnTransformer annotation which was added exactly for this type of scenarios.

Therefore, the Vault mapping looks like this:

@Entity(name = "Vault")
public class Vault {

    @Id
    private Long id;

    @ColumnTransformer(
        read =  "pgp_sym_decrypt(" +
                "    storage, " +
                "    current_setting('encrypt.key')" +
                ")",
        write = "pgp_sym_encrypt( " +
                "    ?, " +
                "    current_setting('encrypt.key')" +
                ") "
    )
    @Column(columnDefinition = "bytea")
    private String storage;

    //Getter and setters omitted for brevity
}

Because hard-coding the encryption key in the mapping does not sound like a very good idea, we will use the PostgreSQL support for user-defined settings instead.

So, the encrypt.key is stored in postgresql.conf configuration file:

encrypt.key = 'Wow! So much security.'

Testing time

When persisting a Vault entity:

Vault user = new Vault();
user.setId(1L);
user.setStorage("my_secret_key");

entityManager.persist(user);

Hibernate is going to encrypt the column, so if you select it with a native SQL query:

String encryptedStorage = (String) entityManager
.createNativeQuery(
    "select encode(storage, 'base64') " +
    "from Vault " +
    "where id = :id")
.setParameter("id", 1L)
.getSingleResult();

LOGGER.info("Encoded storage: {}", encryptedStorage);

You are going to see a value like this:

-- Encoded storage: ww0EBwMC3If4VmIUn2x+0j4BKrKR9j0GFpg87Qoz/v21etflhGPE6l9p7O5Sz9yOhynbvr+gwncW

However, when loading the entity with Hibernate:

Vault vault = entityManager.find( Vault.class, 1L );
assertEquals("my_secret_key", vault.getStorage());

The storage attribute is properly decrypted back to the original value.

If you enjoyed this article, I bet you are going to love my book as well.

Conclusion

As I explained in my book, High-Performance Java Persistence, if you don’t take advantage of the underlying JPA provider or relational database capabilities, you are going to lose lots of features, like easy-peasy encryption.

If you liked this article, you might want to subscribe to my newsletter too.

Advertisements

2 thoughts on “How to encrypt and decrypt data with Hibernate

  1. Hello Vlad, great article.

    However, I guess it would be better to decrypt/encrypt data in one of JPA layers, as in your approach data is decrypted at db level and transported to application in plain text. What do you think ?

    Andrei

    1. Usually, you need several layers of protection, like encrypted hardware to ensure that backups don’t expose sensitive info, and also a SSL connection which solves the issue you mentioned here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s