Integrating Jasypt with Hibernate 3

Jasypt provides an integration package (org.jasypt.hibernate.type) which provides several Hibernate UserType implementations to allow one or several of the properties in a mapped Hibernate entity to be declared as being of an encrypted type. Types allowed to be stored encrypted include strings, binaries (byte arrays), numeric types, booleans and dates.

Persistence of those properties will be done in an encrypted manner, but in a completely transparent way for the application.

This can be useful for encrypting personal data, private messages, etc, so that it is avoided that anyone with read access to the "critical" tables can read all of its contents.

For encryption at Hibernate, jasypt uses its password-based encryption capabilities, and any encryptor object implementing PBEStringEncryptor, PBEByteEncryptor, PBEBigIntegerEncryptor or PBEBigDecimalEncryptor can be used to encrypt data, even encryptors created by the user.

But encryption sets a limitation on your Hibernate usage: security standards establish that two different encryption operations on the same data should not return the same value (due to the use of a random salt). Because of this, none of the fields that are set to be encrypted when persisted can be a part of a WHERE clause in your search queries for the entity they belong to.

Encryption types

Java typeSQL typeJasypt Hibernate type
(org.jasypt.hibernate.type)
Specific features
StringVARCHAR, CLOB, TEXTEncryptedStringType
byte[]VARBINARY, BLOBEncryptedBinaryTypeHonors hibernate.jdbc.use_streams_for_binary.
ByteVARCHAR, CLOB, TEXTEncryptedByteAsStringType
ShortVARCHAR, CLOB, TEXTEncryptedShortAsStringType
IntegerVARCHAR, CLOB, TEXTEncryptedIntegerAsStringType
LongVARCHAR, CLOB, TEXTEncryptedLongAsStringType
BigIntegerNUMERIC, NUMBEREncryptedBigIntegerType
BigIntegerVARCHAR, CLOB, TEXTEncryptedBigIntegerAsStringType
FloatVARCHAR, CLOB, TEXTEncryptedFloatAsStringType
DoubleVARCHAR, CLOB, TEXTEncryptedDoubleAsStringType
BigDecimalNUMERIC, NUMBEREncryptedBigDecimalTypeNeeds an additional decimalScale property.
BigDecimalVARCHAR, CLOB, TEXTEncryptedBigDecimalAsStringType
BooleanVARCHAR, CLOB, TEXTEncryptedBoleanAsStringType
DateVARCHAR, CLOB, TEXTEncryptedDateAsStringType
CalendarVARCHAR, CLOB, TEXTEncryptedCalendarAsStringTypeOffers an additional storeTimeZone property.

When storing an encrypted field, note that you will need a higher amount of space to store the result of the encryption than the space you would need to store the unencrypted piece of data. This is specially important when encrypting with EncryptedBigIntegerType or EncryptedBigDecimalType, as NUMBER and NUMERIC fields are defined by means of digits, not bytes, and thus the difference of size will be much higher than intuitively expected (normally you won't use a field smaller than a NUMERIC(60) to store an encrypted normal-size BigInteger).

Configuration

Setting jasypt to encrypt data with hibernate requires two separate actions:

  1. Configuring the Hibernate mapping.
  2. Providing the encryptor to Hibernate.

Configuring the Hibernate mapping

The Hibernate mapping file can be configured in two different ways:

  1. By extension, declaring the encryptor parameters in the mapping itself (password, algorithm, providerName, etc). This is not very recommended (the mapping file does not seem like a right place for a password) but can be useful in some situations. When this method is selected, the encryptor implementation used is always a StandardPBEStringEncryptor.
      <hibernate-mapping package="myapp">
        ...
        <typedef name="encryptedString" class="org.jasypt.hibernate.type.EncryptedStringType">
          <param name="algorithm">PBEWithMD5AndTripleDES</param>
          <param name="password">jasypt</param>
          <param name="keyObtentionIterations">1000</param>
        </typedef>
        ...
        <class name="UserData" table="USER_DATA">
          ...
          <property name="address" column="ADDRESS" type="encryptedString" />
          ...
        <class>
        ...
      <hibernate-mapping>
    

    Note: only "password" is a required parameter.\ If this configuration method is used, it is not necessary to provide an encryptor to Hibernate (like explained below).

  2. By using a registered encryptor, which will be registered with a specific name. This way, all we will have to use in the mapping file is this name (encryptorRegisteredName).
      <hibernate-mapping package="myapp">
        ...
        <typedef name="encrypted" class="org.jasypt.hibernate.type.EncryptedStringType">
          <param name="encryptorRegisteredName">strongHibernateEncryptor</param>
        </typedef>
        ...
        <class name="UserData" table="USER_DATA">
          ...
          <property name="address" column="ADDRESS" type="encrypted" />
          ...
        <class>
        ...
      <hibernate-mapping>
    

Providing the encryptor to Hibernate

If we have chosen to use the encryptorRegisteredName parameter in our Hibernate mapping file, we will need to bind somehow an encryptor object to that name.

This can be done in two different manners, depending on whether we are using an IoC container like Spring or not.

  • Using Spring we simply need to create our encryptor, and then a HibernatePBEEncryptor object to wrap it and set a registered name for it:
      <bean id="strongEncryptor"
        class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
        <property name="algorithm">
            <value>PBEWithMD5AndTripleDES</value>
        </property>
        <property name="password">
            <value>jasypt</value>
        </property>
      </bean>
      
      <bean id="hibernateStringEncryptor"
        class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor">
        <property name="registeredName">
            <value>strongHibernateStringEncryptor</value>
        </property>
        <property name="encryptor">
            <ref bean="strongEncryptor" />
        </property>
      </bean>
    

    Also, we can do it all in the hibernateStringEncryptor declaration, if we don't want to use the strongEncryptor outside of Hibernate and we are OK with the standard encryptor implementation:

      <bean id="hibernateStringEncryptor"
        class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor">
        <property name="registeredName">
            <value>strongHibernateStringEncryptor</value>
        </property>
        <property name="algorithm">
            <value>PBEWithMD5AndTripleDES</value>
        </property>
        <property name="password">
            <value>jasypt</value>
        </property>
      </bean>
    
  • Without Spring, we will have to use the HibernatePBEEncryptorRegistry singleton directly, registering our encryptor at application initialization like this (for example, inside a ServletContextListener for a webapp):
      StandardPBEStringEncryptor strongEncryptor = new StandardPBEStringEncryptor();
      ...
      HibernatePBEEncryptorRegistry registry =
          HibernatePBEEncryptorRegistry.getInstance();
      registry.registerPBEStringEncryptor("strongHibernateStringEncryptor", strongEncryptor);
    

Using annotations

Alternatively, we may have chosen to use annotations for mapping our entities instead of separate XML mapping files. If it has been so, we can still use jasypt to encrypt our data.

All we will have to do is, first, define the encryption type with a @TypeDef annotation, which could be either inside the persisted entity class or inside a @TypeDefs declaration in a separate package-info.java file:

@TypeDef(
    name="encryptedString", 
    typeClass=EncryptedStringType.class, 
    parameters= {
        @Parameter(name="encryptorRegisteredName", value="myHibernateStringEncryptor")
    }
)

...and then simply map our property with the already declared type:

    @Type(type="encryptedString")
    public String getAddress() {
        return address;
    }