Introduction
The Grails Jasypt Encryption plugin provides strong field-level encryption support on Grails GORM String fields. It leverages the Jasypt simplified encryption framework that makes working with the Java Cryptographic Extension (JCE) much easier.
It also comes with the Bouncy Castle encryption provider, which gives you access to the very secure AES algorithm.
License
This plugin is released under the Apache License, Version 2.0
Acknowledgments
Following are the plugin contributors:
Source code
The full source code for this plugin can be found on GitHub.
For issues, improvements or new features go to the plugin’s GitHub issues.
Usage
Installation
Grails 8 supports both Hibernate 5 and Hibernate 7, so the plugin comes in two flavours. Add the one matching the GORM Hibernate version of your app:
| GORM | Dependency |
|---|---|
Hibernate 5 ( |
|
Hibernate 7 ( |
|
For example, for an app using Hibernate 5:
dependencies {
implementation "io.github.gpc:grails-jasypt-encryption-hibernate5:6.0.0"
}
Both flavours depend on grails-jasypt-encryption-core, which contains the Grails plugin and the shared configuration.
They provide the same encrypted types with the same class names, they are configured the same way, and they store
the encrypted values in the same format. You can therefore switch an app from Hibernate 5 to Hibernate 7 by
switching the dependency, without changing your domain classes or re-encrypting your data.
Upgrading from version 5
Up to version 5 the plugin was published as io.github.gpc:grails-jasypt-encryption. From version 6, that artifact
is only a relocation to grails-jasypt-encryption-hibernate5, so an existing build depending on
implementation "io.github.gpc:grails-jasypt-encryption:6.0.0"
resolves to the Hibernate 5 flavour. Please replace it with grails-jasypt-encryption-hibernate5, or with
grails-jasypt-encryption-hibernate7 when your app uses Hibernate 7.
Requirements for Grails 8:
-
Java 21 or higher
-
Gradle 9.6 or higher
Previous versions
-
If your app is using Grails 7, then use version 5.0.0 of
io.github.gpc:grails-jasypt-encryption(Java 17 or higher, Gradle 8.5 or higher). -
If your app is using Grails 4, then use version 4.0.3 of this plugin.
No longer supported
-
If your app is using Grails 3 or higher, then use version 2.0.2 of this plugin.
-
If your app is using Grails 2 and Hibernate 4, then use version 1.3.1 of this plugin.
-
If your app is using Grails 2 and Hibernate 3, then use version 1.2.1 of this plugin.
Configuration
You’ll then need to configure the encryption in your Grails configuration using a stanza like this (make sure to change the password):
jasypt {
algorithm = "PBEWITHSHA256AND256BITAES-CBC-BC"
providerName = "BC"
password = "<your very secret passphrase>"
keyObtentionIterations = 1000
}
This will configure your encryption to use a 256 bit AES algorithm to do the encryption. Key generation will also be repeated 1000 times (to slow down any brute force attacks). Changing any of these values will result in different results (and will also prevent the ability to decrypt previously encrypted information).
External Config Files in Grails
You can put the encryption configuration in Config.groovy, but a better location would be an external configuration file that does not get checked into source code (and can vary based on environment). Just put something like this in your Config.groovy file to define an external config file:
def configFilePath = System.getenv('ENCRYPTION_CONFIG_LOCATION') ?: "file:${userHome}/.jasypt"
grails.config.locations = [configFilePath]
This enables you to set an environment variable with the location of the encryption configuration, otherwise it will look for a .jasypt file in the current user’s home (useful for developers).
Enabling "Unlimited" Encryption in Java
Because of American export standards, and the notion that cryptographically strong algorithms are "munitions", you’ll want to make sure that the encryption policy jars that came with your installation of Java allow "unlimited" encryption, rather than the default "strong" (which are actually pretty weak).
To check to see what level of encryption your installation of Java has, you’ll need to do one of two things:
-
Run the code from this gist on github
-
Crack open your
$JAVA_HOME/jre/lib/security/local_policy.jarto see what’s inside:% cd /tmp % cp $JAVA_HOME/jre/lib/security/local_policy.jar . % jar xvf local_policy.jar inflated: META-INF/MANIFEST.MF inflated: META-INF/JCE_RSA.SF inflated: META-INF/JCE_RSA.RSA created: META-INF/ inflated: default_local.policy % cat default_local.policy // Country-specific policy file for countries with no limits on crypto strength. grant { // There is no restriction to any algorithms. permission javax.crypto.CryptoAllPermission; };
If the permissions look like that, you’re set. Mac laptops bought in the US have this configured by default, all other installations have needed to be upgraded (including all Linux distros).
Installing the new jar files is easy, just go download the "Java Cryptography Extension (JCE)" under "Other Downloads" on Oracle’s website.
Unzip the zip file and copy the jar files into your $JAVA_HOME/jre/lib/security directory to overwrite the existing, limited encryption jars.
# make a backup copy of your existing files
mkdir old_java_security
cp -r $JAVA_HOME/jre/lib/security old_java_security
unzip jce_policy-6.zip
sudo cp jce/*.jar $JAVA_HOME/jre/lib/security
Defining Fields in your Domain to Encrypt
Defining fields in your domain object that you want to be encrypted within the database is easy once the plugin is installed and configured, just define the "type" within the domain class' mapping:
package com.bloomhealthco.domain
import com.bloomhealthco.jasypt.GormEncryptedStringType
class Member {
String firstName
String lastName
String ssn
static mapping = {
ssn type: GormEncryptedStringType
}
}
One other caveat, if you’re setting the length of the field within the database schema, you’ll need to give yourself extra room as the encrypted value will be longer than the unencrypted value was. This length will depend on the encryption algorithm that you use. It’s easy to write an integration test that can spit out all of the encrypted lengths for you. See testEncryptionWithLongNamesFit() in the plugin’s sample project for an example.
Custom Encryption Types
The plugin provides "Gorm" versions of all the built in Encrypted types provided by the jasypt plugin, see http://www.jasypt.org/hibernate.html. Jasypt only provides these types for Hibernate 5, so the Hibernate 7 flavour of the plugin implements them itself, storing the values in the same format.
Hibernate 5
It is also possible to define your own GORM encrypted types. This happens in two steps. First, you need a UserType that handles the encryption. This might be a class you have already from an existing Java application or you could extend the jasypt class AbstractEncryptedAsStringType. Second, you define a new Gorm Encrypted type that composes that UserType to provide the wiring to the Grails configuration.
Here’s an example that can encrypt joda-time dates (requires the joda-time jar to work):
package com.bloomhealthco.jasypt
import org.jasypt.hibernate5.type.AbstractEncryptedAsStringType
import org.joda.time.LocalDate
public class EncryptedLocalDateAsStringType extends AbstractEncryptedAsStringType {
protected Object convertToObject(String string) {
if (!string) return null
if (!(string =~ /\d{4}-\d{2}-\d{2}/)) throw new IllegalArgumentException("String does not match YYYY-MM-dd pattern")
new LocalDate(string)
}
protected String convertToString(Object object) {
if (!object) return null
if (object.class != LocalDate) throw new IllegalArgumentException("Expected ${LocalDate.name} but was ${object.class.name}")
object.toString("YYYY-MM-dd")
}
public Class returnedClass() { LocalDate }
}
public class GormEncryptedLocalDateAsStringType extends JasyptConfiguredUserType<EncryptedLocalDateAsStringType> {
}
Hibernate 7
With the Hibernate 7 flavour, extend the plugin’s AbstractEncryptedAsStringType. It wires the encryptor from the
Grails configuration itself, so no additional Gorm type is needed:
package com.bloomhealthco.jasypt
import java.time.LocalDate
class GormEncryptedLocalDateAsStringType extends AbstractEncryptedAsStringType<LocalDate> {
@Override
Class<LocalDate> returnedClass() {
LocalDate
}
@Override
protected LocalDate convertToObject(String string) {
LocalDate.parse(string)
}
@Override
protected String convertToString(LocalDate value) {
value.toString()
}
}
Further Documentation
For now, documentation is a little light. There’s a sample project checked in as part of the repository that shows the plugin being used (and has tests that exercise the functionality). The Patient domain object has encrypted firstName and lastName fields on it.
The Hibernate 7 sample project runs the same sources and tests on Hibernate 7.
Release Notes
-
6.0.0 - Migrated to Grails 8.0.0 (Java 21, Groovy 5). Added support for Hibernate 7. The plugin is now split into
grails-jasypt-encryption-hibernate5andgrails-jasypt-encryption-hibernate7, sharinggrails-jasypt-encryption-core. The formergrails-jasypt-encryptionartifact is relocated tograils-jasypt-encryption-hibernate5 -
5.0.0 - Migrated to Grails 7.0.0
-
4.0.4 - Move to maven central, new group ID, can compile against Grails > 4.0.5
-
4.0.3 - initial support for Grails 4
-
2.0.1 - initial support for Grails 3
-
1.3.1 - initial support for Hibernate 4
-
1.2.1 - code cleanup contribution from Burt Beckwith
-
1.2.0 - support grails 2.3.8 and up
-
1.1.0 - Support all the encrypted UserTypes provided by jasypt’s hibernate support while reusing the existing implementations; thanks to Jon Palmer for pull request & work on this
-
1.0.0 - upgraded to jasypt 1.9 - support for grails 2.0.0 thanks to pull request from Jon Palmer
-
0.1.2 - upgraded to jasypt 1.7 - support for hibernate 3.6 and grails 1.3.6. Could also work with earlier versions of grails but it has only been tested on 1.3.6 currently