Jasypt, Bouncy Castle, Hibernate et Spring: Simplifier la cryptographie en java

Jasypt: did you know?
jasypt-small.png

C'est une API java, open source, qui simplifie grandement le chiffrement et l'anonymisation (hachage).
Jasypt : Java Simplified Encryption: le chiffrement simplifié en java.
Plus d'informations sur le site Jasypt.

Ce qui est intéressant c'est la facilité avec laquelle nous pouvons intégrer les frameworks: Hibernate3, Spring3, Bouncy Castle et Jasypt pour réaliser de manière
totalement transparente le cryptage ou l'anonymisation des (ou de certains) champs de la base de données.

Transparente, c'est à dire?

Justement la suite de ce billet démontre que le code métier ne sera pas impacté ni modifié.
Il suffit de configurer Spring et d'annoter (ou l'équivalent en xml) les entités persistantes avec les annotations d'Hibernate.
Q. C'est tout?
R. Oui, Heu... Peut-être également déclarer de nouveaux types pour Hibernate avec de nouvelles annotations.

Signalons que Jasypt suit les normes Oracle JCA / JCE pour la cryptographie uni-directionnelle (digest) ou bi-directionnelle.
Jasypt s'intègre parfaitement à Spring 2 & 3. Il est même conçu pour s'intégrer de façon thread-safe.
Jasypt est ouvert à tout fournisseur (provider) de JCE, par exemple Bouncy Castle.

Jasypt offre la possibilité de crypter les champs persistants avec Hibernate3 & 4.
L'objet de la démo ci-dessous est d'illustrer ce dernier point.
Pour cela, elle utilise Bouncy Castle Provider pour chiffrer les champs persistants avec notre ORM préféré Hibernate.
Enfin, Jasypt fournit une console qui permet de faciliter les phases de tests/recettes en modifiant directement les données dans la base

Passons au concret. L'environnement est BD Informix (ou toute autre), Spring3, Hibernate3, Bouncy Castle, Eclipse STS, Java6, Maven2. (Rien que çela...!)
Noter que notre thème est proche de celui traité dans mon billet sur filtre et cryptage.

Préparer l'environnement de la démo:

Créer un projet maven sous eclipse avec les dépendances Jasypt :

    <dependency>
        <groupId>org.jasypt</groupId>
        <artifactId>jasypt</artifactId>
        <version>1.7</version>
     </dependency>

   <dependency>
	<groupId>org.jasypt</groupId>
	<artifactId>jasypt-hibernate3</artifactId>
	<version>1.9.0</version>
  </dependency>

Ajouter les dépendances Bouncy Castle:

<!-- Bouncy castle -->
<dependency>
	<groupId>bouncycastle</groupId>
	<artifactId>bcprov-jdk15</artifactId>
	<version>140</version>
</dependency>

Configurer Spring:

<!-- Enccrypt JASYPT -->
 <bean id="myStringEncryptor"
    class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">   
   <property name="providerName"><value>BC</value></property> 
    <property name="algorithm">
        <value>PBEWITHSHA256AND128BITAES-CBC-BC</value> 
    </property>
    <property name="password">
        <value>jasypt</value>
    </property>
  </bean>
   <!-- hibernate Encryptor for string -->
  <bean id="theHibernateStringEncryptor"
   class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor">
	<property name="registeredName">
		<value>myHibernateStringEncryptor</value>
		</property>
		<property name="encryptor">
		<ref bean="myStringEncryptor" />
		</property>
   </bean>

Bien évidemment, la configuration de Spring n'est pas complète. Seule la partie qui nous concerne est présentée ici.
N'oublier pas de configurer les beans de Spring: dataSource, sessionFactory et transactionManager.
Sur le net on trouve facilement des tutoriaux sur le sujet.

Configurer l'entité Hibernate avec des annotations spécifiques:

@TypeDefs
({
@TypeDef ( 
   name="encryptedString",
   typeClass=EncryptedStringType.class,
   parameters={
     @Parameter(name="encryptorRegisteredName",
     value="myHibernateStringEncryptor")
   }
  )
})
@Entity
public class Domaine implements Serializable{
private static final long serialVersionUID = 1L;	
@Id private int id;
private String libelle;
private String description;	
@Type(type="encryptedString")
private String ssn;   
...
... getters / setters / toString omitted

Quelques explications :
L'annotation @TypeDef du package hibernate permet de définir un nouveau type,.
Dans notre démo c'est EncryptedStringType de Jasypt (org.jasypt.hibernate.type.EncryptedStringType).
L'attribut name, "encryptedString", de l'annotation @TypeDef permet d'annoter le (les) champs persistants à crypter/décrypter par Hibernate.
Dans notre cas, la classe entité, Domaine, possède une propriété nommée "ssn" de type String.
Cette propriété est annotée avec @Type(type="encryptedString") afin d'indiquer à Hibernate de la crypter/décrypter.

Aussi dans @DefType, l'attribut name de l'annotaion @Parameter d'hibernate établit le lien entre l'algorithme de cryptage enregistré dans la configuration de Spring.

Ecrire un test JUnit:

@Test public void testPersitColumnWithJasyptBC(){
   ClassPathXmlApplicationContext springContext = 
		new ClassPathXmlApplicationContext("classpath:spring.xml");
  <!-- add dynamincally BC provider -->
  Security.addProvider(new BouncyCastleProvider());
		
  IDomaineService domServ = (IDomaineService) 
            springContext.getBean("domaineService");	 
  Domaine domaine=new Domaine();
  domaine.setId(1);
  domaine.setDescription("dommaine1 exemple");
  domaine.setLibelle("dom1");
  domaine.setSsn("ssn2crypte");
  int pkDomaine= (Integer) domServ.save(domaine);
  Assert.assertTrue(pkDomaine==DOMAINE_ID);
  Domaine domFound=domServ.findDomaineById(DOMAINE_ID);
  Assert.assertTrue(domFound.getId()==DOMAINE_ID 
       && "ssn2crypte".equals(domFound.getSsn()));
}

Avec un peu de chance et une bonne configuration de log4j.xml (voir mon billet sur ce sujet), vous aurez les traces ci-dessous :
Hibernate:

   insert 
   into
       domaine
       (description, libelle, ssn, id) 
   values
       (?, ?, ?, ?)

TRACE- binding 'dommaine1 exemple' to parameter: 1
TRACE- binding 'dom1' to parameter: 2
TRACE- binding '1' to parameter: 4
Hibernate:

   select
       domaine0_.id as id0_0_,
       domaine0_.description as descript2_0_0_,
       domaine0_.libelle as libelle0_0_,
       domaine0_.ssn as ssn0_0_ 
   from
       domaine domaine0_ 
   where
       domaine0_.id=?

TRACE- binding '1' to parameter: 1
....

Disons que tout n'a pas été expliqué dans ce billet car beaucoup de notions sont traitées ici.
J'espère néanmoins que c'est suffisamment clair.

4 commentaires

  1. La date de génération du build est très utile en développement, on se demande souvent à quelle heure l’application a été buildée.
    Peut-être moins utile en production, quoiqu’on peut utiliser l’année de la version du logiciel livré dans un copyright par exemple.

  2. Bonjour,

    J’ai essayé de mettre en place votre solution, mais je rencontre l’erreur suivante :

    Exception in thread « main » java.lang.NoClassDefFoundError: org/hibernate/util/EqualsHelper
    at org.jasypt.hibernate.type.AbstractEncryptedAsStringType.equals(AbstractEncryptedAsStringType.java:92)

    Mes recherches ont été jusqu’à présent infructueuses. Auriez-vous une piste ?

    Merci d’avance,
    Julien Neuhart

  3. Il suffit d’ajouter la dépendance hibernate-core-3xxxFinal.jar dans le classpath du projet

  4. En effet, je dois être fatigué…

    Dans tous les cas, ça fonctionne parfaitement maintenant.

    Merci encore pour votre tutoriel !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.