mardi 5 août 2008

Howto crypt / uncrypt using JAVA and RSA keys

Problématique :

Dans le cadre d'un bus de données nous souhaitons sécuriser les données en transit. Comme il s'agit d'un bus de transfert asynchrone, lorsque des messages sont stockés sur les files JMS il doivent également être crypté pour n'apparaitre en clair qu'au consommateur. Il n'est pas possible de se servir de SSL puisque le cryptage ne concerne pas que les phase de transport.

Nous allons réaliser le cryptage au niveau des producteurs et consommateurs du bus en nous servant des fonctionnalités venant avec le JDK 5,0.

La principale limitation dans le cryptage JAVA vient de la profusion des différents formats. Pour ce faire nous allons nous servir d'un cryptage à partir de clé publique/clé privée.

La génération des fichiers clé privée et clé publique se fait à l'aide de openssh.


openssl genrsa -aes256 -out /public/cleprivee.pem 2048
openssl rsa -in /public/private.pem -pubout -outform DER -out clepublique.der
openssl pkcs8 -topk8 -inform PEM -in /public/cleprivee.pem -outform DER -nocrypt -out /public/cleprivee.pk8


Le format PK8 est le seul disponible nativement en JAVA, il sert pour la lecture des clés privées


Le format DER est supporté pour les clé publique en JAVA, il sert pour la lecture des clés publique.


Le code suivant crypte et décrypte un message « Mon Cul C'est du poulet » à partir d'une clé publique et privée lue sur dans des fichiers :




// Lecture du fichier de clé publique
File keyFile = new File("clepublique.der");
byte[] encodedKey = new byte[(int) keyFile.length()];
new FileInputStream(keyFile).read(encodedKey);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);

// Encryptage à l'aide de la clé publique
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cypherText = cipher.doFinal("Mon Cul C'est du poulet"
.getBytes("UTF8"));

// Affichage du résultat du cryptage
System.out.println("----- Bytes cryptés --------");
System.out.write(cypherText);
System.out.println();
System.out.println("----------------------------");

// Lecture de la clé privée
keyFile = new File("cleprivee.pk8");
encodedKey = new byte[(int) keyFile.length()];
new FileInputStream(keyFile).read(encodedKey);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
PrivateKey privateKey = (RSAPrivateKey) kf
.generatePrivate(privateKeySpec);
cipher.init(Cipher.DECRYPT_MODE, privateKey);

// Affichage du résultat décrypté
System.out.println("----- Bytes décryptés ------");
byte[] clearText = cipher.doFinal(cypherText);
System.out.write(clearText);
System.out.println();
System.out.println("----------------------------");



Ce code ne fonctionne pas quand les données sont trop longue car elle dépasse la taille de la clé. Dans ce cas pour des raison de performances, il vaut mieux utiliser des cryptage symétrique à l'aide de mot de passe. Le code qui suit en donne un illustration.

/** Le chiffreur */
Cipher cipher;

/** La cle de cryptage */
SecretKey secretKey;

public void init(String password) throws Exception {
cipher = Cipher.getInstance("DES");
keyFactory = SecretKeyFactory.getInstance("DES");
DESKeySpec privateKeySpec = new DESKeySpec(password.getBytes());
secretKey = keyFactory.generateSecret(privateKeySpec);
}

/**
* Crypte les données
*
* @return
* @throws Exception
*/

public byte[] encrypt(byte[] aCrypter) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream bais = new ByteArrayInputStream(aCrypter);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
CipherOutputStream cis = new CipherOutputStream(baos, cipher);
byte[] buffer = new byte[1024];
int read;
while ((read = bais.read(buffer)) != -1) {
byte[] buffer2 = cipher.update(buffer, 0, read);
baos.write(buffer2);
}
baos.write(cipher.doFinal());
buffer = baos.toByteArray();
return buffer;
}
/**
* Décrypte les données
*
*/

public byte[] decrypt(byte[] aDecripter) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream bais = new ByteArrayInputStream(aDecripter);
cipher.init(Cipher.DECRYPT_MODE, secretKey);

byte[] buffer = new byte[1024];
int read;
while ((read = bais.read(buffer)) != -1) {
byte[] buffer2 = cipher.update(buffer, 0, read);
baos.write(buffer2);
}
baos.write(cipher.doFinal());
buffer = baos.toByteArray();
return buffer;
}

4 commentaires:

PJ a dit…

Salut Clément

Ton code fonctionne t'il avec de gros messages ? ( plusieurs fois la taille de la clé )

Clement Soullard a dit…

Non, il ne fonctionne que pour les petits messages. J'ai ajouté le code pour les plus gros messages.

Anonyme a dit…

Thanks for sharing this link, but argg it seems to be offline... Does anybody have a mirror or another source? Please answer to my message if you do!

I would appreciate if a staff member here at www.blogger.com could repost it.

Thanks,
Alex

Anonyme a dit…
Ce commentaire a été supprimé par un administrateur du blog.