juin
2008
Je me suis frotté récemment au problème de cryptage sous java.
Pour cela, j’avais quelques contraintes :
- je n’ai pas grand chose de connaissances en matière de cryptographie
- mon programme doit tourner sous une JDK 1.3 (oui … la honte)
Pour ce qui est des concepts généraux, je ne peux que remercier Razgriz de la présentation qu’il m’en à fait.
Pour ce qui est de la compatibilité avec un JDK 1.3, mes recherches m’ont fait aboutir sur les librairies Bouncy Castle qui ont le bon gout d’être compatible avec de vieilles versions java (et d’utilisation gratuite).
Bref, les principes généraux acquis et la solution identifiée, je ne voyais plus ce qui pouvait me poser problème … et pourtant.
Bouncy Castle fourni tout ce qu’il faut pour une mise en œuvre, mais pour que celle-ci se réalise sans problème, il doit falloir un peu plus de connaissances (du point de vue technique) que mon tour d’horizon des principes généraux.
Je dois admettre avoir eu beaucoup de mal à faire le lien entre les exemples fournis et l’application pratique que je souhaitais en faire : Crypter et signer un fichier à l’aide de PGP.
En s’appuyant sur les exemples fournis, le premier est assez facile, le second aussi, mais le mix des deux est plus problématique.
Bref, j’ai tourné en rond pendant pas mal de temps, jusqu’à ce qu’une âme charitable m’offre un bout de code dont la simplicité à pour vertu de lever le voile sur les subtilités d’enchaînement de ces 2 étapes.
Avec son accord, je vous publie ici le code que Viet H. Phan (l’âme charitable pré-citée) m’a fourni :
import java.io.OutputStream;
import java.util.Date;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPV3SignatureGenerator;
public class BCExample {
private String option;
private boolean isOldPGPFormat;
private boolean mustArmor;
private PGPCompressedDataGenerator compressor;
private PGPSignatureGenerator v4signer;
private PGPV3SignatureGenerator v3signer;
private PGPOnePassSignature onePassSignature;
private PGPEncryptedDataGenerator encryptor;
private byte[] armor(byte[] inputData) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ArmoredOutputStream aOut = new ArmoredOutputStream(out);
aOut.write(inputData);
aOut.close();
return out.toByteArray();
}
private byte[] literalize(byte[] inputData) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PGPLiteralDataGenerator dGen = new PGPLiteralDataGenerator(this.isOldPGPFormat);
OutputStream dOut = dGen.open(out, PGPLiteralData.BINARY, "", inputData.length, new Date());
dOut.write(inputData);
dGen.close();
return out.toByteArray();
}
private byte[] compress(byte[] inputData) throws Exception {
if(this.compressor != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream cOut = this.compressor.open(out);
cOut.write(inputData);
this.compressor.close();
return out.toByteArray();
}
return inputData;
}
private byte[] sign(byte[] inputData) throws Exception {
if(this.v4signer != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
if(this.onePassSignature != null) {
this.onePassSignature.encode(out);
out.write(literalize(inputData));
this.v4signer.update(inputData);
this.v4signer.generate().encode(out);
} else {
this.v4signer.update(inputData);
this.v4signer.generate().encode(out);
out.write(literalize(inputData));
}
return out.toByteArray();
} else if(this.v3signer != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
if(this.onePassSignature != null) {
this.onePassSignature.encode(out);
out.write(literalize(inputData));
this.v3signer.update(inputData);
this.v3signer.generate().encode(out);
} else {
this.v3signer.update(inputData);
this.v3signer.generate().encode(out);
out.write(literalize(inputData));
}
return out.toByteArray();
}
return inputData;
}
private byte[] encrypt(byte[] inputData) throws Exception {
if(this.encryptor != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream eOut = this.encryptor.open(out, inputData.length);
eOut.write(inputData);
this.encryptor.close();
return out.toByteArray();
}
return inputData;
}
public byte[] process(byte[] inputData) throws Exception {
if(this.option.equals("sign")) {
inputData = sign(inputData);
inputData = compress(inputData);
} else if(this.option.equals("encrypt")) {
inputData = literalize(inputData);
inputData = compress(inputData);
inputData = encrypt(inputData);
} else if(this.option.equals("sign-then-encrypt")) {
inputData = sign(inputData);
inputData = compress(inputData);
inputData = encrypt(inputData);
}
if(this.mustArmor) {
inputData = armor(inputData);
}
return inputData;
}
}
L’exemple est volontairement dépouillé des getter/setter permettant l’initialisation du contexte afin de se focaliser sur l’enchaînement nécessaire des éléments, mais il fonctionne parfaitement.
Si vous souhaitez vous plonger dans l’utilisation de cette librairie, voici quelques liens vous seront bien utiles :
- http://www.bouncycastle.org/fr/index.html : Le site en lui-même.
- dev-crypto-request@bouncycastle.org : la liste de diffusion à travers laquelle Viet H. Phan m’est venu en aide. Vis-à-vis des divers demandes publiées jusqu’ici, j’ai toujours eu des éléments de réponse dans des délais très raisonnables. (Cf. Le site pour les instructions d’abonnement)
- http://www.nabble.com/Bouncy-Castle-f943.html : un archivage de la liste de diffusion précédemment citée sur lequel on peut effectuer des recherches.
Bonne chance !