We will start by creating a Spring Boot project using Spring Data JPA and H2 embedded database.
1.1. Maven Dependencies
First we need to add the dependencies to
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Next, we create an entity class to represent
User domain object:
Now we can create a simple repository which will allow us to perform various operations with
Our initial project is ready and we can proceed to the implementation of encryption.
2. Attribute Converter
The most optimal solution using JPA would be to implement column-level encryption using the
Attribute Converter introduced in JPA 2.1.
Another option would be to use JPA listeners, which is available for older versions of JPA and has its advantages,
but the final implementation is more complex and not reusable, so we will not consider it in this article.
First we need to implement
AttributeConverter, which will encrypt and decrypt the attributes.
In our implementation, it will use AES algorithm provided by JDK:
Next, we need to add
@Convert annotation to the attribute that we want to encrypt, and specify the converter:
That is all we need to do to implement column-level encryption using
Let’s look at another implementation option and then test them together.
3. Database Function
Most databases provide easy to use functions to apply strong encryption algorithms.
Unfortunately, the JPA standard does not offer any options to use these functions, but as long as we adhere to the Spring’s default JPA provider,
which is Hibernate, we can use its proprietary features.
One of these features is
@ColumnTransformer annotation which allows to provide a custom SQL expression which is used by Hibernate to read the value from and write a value to a column.
We are going to use this annotation to invoke database encryption functions.
In the case of the H2 database we are using, we will have to use a set of several functions that look pretty ugly:
But for most other databases, annotation will look better. For example, when using PostgreSQL:
Note: A small drawback in this case is that our entity attribute is of type
String, but PostgreSQL wants to store the encrypted data in a column of type
so this can become an issue for our example since we generate a database table based on the entity, but in any case, for real projects, generating tables in this way is not recommended.
4. Test encryption
We set up encryption for the two attributes using different methods, but we need to make sure that they work correctly. Let’s start by creating a test class that will save a user to the database before running tests and set the user ID in a variable:
Next, we can verify that when we read this user from the database using the repository, all values are in place and are correct:
Finally, we can read the values stored in the database, bypassing our repository, and make sure that they do not match our original values:
And to satisfy our curiosity, we also output the values from the database to the console:
We learned how to implement column-level encryption in several ways, but we must not forget about the risks associated with this process, most of which are related to key management. For example, insecure storage of keys can give people with malicious intent the ability to decrypt sensitive data using keys that they have access to, and the loss of keys in most cases entails data loss, since decryption without keys is almost impossible.
Full source code can be found on GitHub.