Réception de mail crypté S/MIME avec Javamail-crypto

Crypto stub
Image via Wikipedia

Dans le billet précédent je montrais comment envoyer un mail S/MIME, ce billet traite de l’opération inverse. Comment réceptionner un mail crypté et signé en S/MIME ?

L’idée est de reprendre la méthode de lecture du certificat publique du signataire pour vérifier la signature du mail mais il faut auparavant décrypter le mail reçu. En effet l’expéditeur signe le message puis le crypte donc on doit décrypter puis vérifier la signature une fois que le mail est devenu lisible.
Voici le pseudo-code

MimeMessage m = retrieveMessageFromMailBox();
m = decryptMessage(m);
checkSignature(m);

La lecture du message dans la boite mail ne pose pas de grande difficulté mais le résultat est un mail avec comme content-type la valeur application/pkcs7-mime. Malheureusement le code utilitaire SMIMEEncryptionUtils de javamail-crypto (qui décrypte un message contient un oubli qui l’empêche de gérer les mails cryptés contenant des PJ comme c’est le cas dans un mail signé. En effet le résultat du décryptage doit être un mail avec le content-type valant multipart/signed ou application/pkcs7-mime ou bien application/x-pkcs7-mime, sinon le mail n’est pas détecté comme un mail signé. Or le méthode qui décrypter decryptMessage.decryptMessage()oublie malheureusement d’invoquer saveChange()sur le message. Aussi le le content-type n’est pas mis à jour. So do it yourself !

Ensuite il suffit d’appeler la méthode utilitaire de vérification de signature SMIMEEncryptionUtils.checkSignature() pour finir la partie réception d’un S/MIME. Il est a noté que le mail signé par SMIMEEncryptionUtils.signeMessage() est de type multipart/signed ce qui signifie que le mail est constitué de 2 partie :

  1. le contenu message original avant signature
  2. la signature portant sur le message original précédent

Il faut en tenir compte pour extraire les informations du message original. C’est-à-dire que le contenu du mail décrypté que l’on obtient par MimeMessage.getContent() pourra être itéré mais on ne s’intéressera qu’à sa première partie. Cette première partie obtenue par Multipart.getBodyPart() sera soit considéré comme  le contenu du corps du message original avant signature si son content-type est text/plain mais si le message original avait déjà des pièces-jointes le content-type sera multipart/mixed et il faudra alors itérer sur la première partie. La forme du message original dépend du client mail utilisé pour créer le message original avant signature et cryptage.

Encrytage de mail avec JavaMail-Crypto et BouncyCastle

Icon from Nuvola icon theme for KDE 3.x.
Image via Wikipedia

Javamail-crypto est une API peu récente puisque le jar est estampillé juin 2006. Cepedant l’usage de cette API simplifie grandemant la vie du programmeur puisque pourvu que le message en clair, la session mail, ainsi que la clé soient fournis, il suffit de faire

EncryptionUtils smimeUtils = null;
String type = EncryptionManager.SMIME;
smimeUtils = EncryptionManager.getEncryptionUtils(type);
smimeSignedMsg = smimeUtils.encryptMessage(session, mmessage,
smimeKey);

Lorsque l’on doit envoyer un mail crypté à la norme S/MIME, on utilise la clé publique du destinataire. On ne peut pas utiliser Javamail-crypto et Bouncycastle comme on le ferait dans le cas de la signature comme dans l’exemple suivant:

char[] smimePw = new String("mot de passe")
.toCharArray();
EncryptionKeyManager smimeKeyMgr = smimeUtils.createKeyManager();
InputStream privKeyIS = null:
smimeKeyMgr.loadPrivateKeystore(privKeyIS, smimePw);
smimeKey = smimeKeyMgr.getPrivateKey("cle privee signature",
smimePw);

En effet bien que l’on puisse charger les clé publiques, l’API s’attend à lire en entrée un fichier PKCS12. Or ce fichier doit aussi contenir la clé privée ce que le destinataire du mail ne va jamais nous fournir. Il se bornera à nous fournir un certificat contenant sa clé publique.

Heureusement que l’API de cryptage prend une clé java.security.Key , il nous suffit de créer un tel objet. Pour cela il ne suffit par de créer un objet implémentant l’interface Key car le code de SMIMEEncryptionUtils.encryptMessage() fait un transtypage:

BouncySMIMEEncryptionKey bKey = (BouncySMIMEEncryptionKey) key;

Nous devons donc créer de toute pièce une instance de BouncySMIMEEncryptionKey.Cela est assez facile car en regardant le code on voit qu’il nous suffit, pour encryptage de mail, de fixer le certificat dans un objet BouncySMIMEEncryptionKey.

Voici le code complet pour lire la clé publique dans un fichier PEM et encrypter un mail:

EncryptionUtils smimeUtils = null;
BouncySMIMEEncryptionKey smimeKey = null;
String type = EncryptionManager.SMIME;
smimeUtils = EncryptionManager.getEncryptionUtils(type);
PEMReader reader = new PEMReader(new InputStreamReader(inputStremoOnThePEM));
X509CertificateObject cert = (X509CertificateObject) reader.readObject();
smimeKey = new BouncySMIMEEncryptionKey();
smimeKey.setCertificate(cert);
MimeMessage smimeSignedMsg = smimeUtils.encryptMessage(session, mmessage,smimeKey);

Envoi de mail sécurisé en Java

Category:WikiProject Cryptography participants

Nous sommes dans le cadre du standard S/MIME avec 2 clés: une pour la signature et l’autre pour le cryptage.

Principe général

L’expéditeur veut être sûr que son message ne sera pas modifié pendant le transfert (internet n’est pas sûr…) Et que seul le destinataire pourra lire le message.

On recherche donc l’authenticité et la confidentialité.

  1. Signature : l’expéditeur signe le message avec sa clé privée.
  2. Cryptage : après avoir signé le message, l’expéditeur le crypte avec la clé publique du destinataire.

Outils

Java SE contient désormais les extension cryptographique mais l’API optionnelle JavaMail ne prend pas en charge la sécurité. Le programmeur doit donc transformer le message en clair en le passant en “multipart” pour y insérer la signature puis crypter les différentes parties et gérer les entêtes du mail pour indiquer au destinataire qu’il s’agit d’un mail crypté et signé.

JavaMail

La version courante 1.4.3 permet d’envoyer un mail en se connectant à un serveur SMTP. Nous utilisons en également les capacités de JavaMail à gérer les pièces jointes ainsi que les entêtes du mail.

Javamail-crypto

Cette librairie permet d’ajouter à JavaMail la prise en charge du S/MIME. JavaMail-Crypto s’appuie sur 2 founisseurs d’implémentation: BouncyCastle pour SMIME et Cryptix pour PGP. Seul le premier sera utilisé dans notre cas.

OpenSSL

OpenSSL est une librairie et des outils pour créer des clés et certificats cryptographique et les manipuler dans un contexte pratique (chriffrage…). Disponible sur tous les Unix il est également utilisable avec Cygwin (pour ceux qui sont du côté obscure…)