Comment signer ses emails avec la norme DKIM ?

Aller, un petit article sur la sécurité informatique pour changer ! Récemment, pour certifier les emails envoyés aux membres du site nageurs.com, l’ajout d’un mécanisme de signature a été mis en place basé sur la norme DKIM.

Cette norme DKIM pour DomainKeys Identified Mail permet de lutter contre le phishing, et surtout de garantir que l’email n’a pas été modifié entre le moment où il a été envoyé ou reçu grace à un mécanisme de signature électronique.

Les objectifs étaient d’améliorer les taux de délivrabilité des emails envoyés, ne pas tomber dans les boîtes spam, et aussi faire apparaître sur Gmail la page Google+ à droite des messages envoyés par le site. Et surtout par curiosité et défi !

On peut le voir sur l’image ci-dessous, le mail est correctement signé (signed-by: nageurs.com).

N’ayant pas trouvé d’article complet sur le sujet, et notamment sur le mécanisme de signature à la main des emails, j’ai décidé d’en faire un petit tutoriel qui explique toutes les étapes, et de vous proposer ici. La lecture (en diagonable bien entendu) de la RFC 4871 est essentielle pour comprendre toutes les subtilités de cette norme, et je vous conseille d’y jeter un coup d’oeil pour prendre connaissance d’autres méthodes de chiffrement, d’algorithmes de concaténation, et de fonction de hashage que je n’évoquerai pas dans cet article.
Voici maintenant les étapes à suivre pour chiffrer des emails. La première étape est de générer une paire de clés : une publique et l’autre qui restera privée. Pour cela il suffit de lancer les commandes suivantes :

openssl genrsa -out rsa.private 1024
openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM

Dans les DNS du serveur ajouter une zone DKIM pour le domaine dkim._domainkey.nageurs.com (vous pouvez remplacer dkim par autre chose, mais garder la chaîne _domainkey). Comme valeur mettre k=rsa; p=[la clé publique sur une seule ligne sans espace]

Il faut bien entendu retirer les lignes « —–BEGIN PUBLIC KEY—– » et « —–END PUBLIC KEY—– » pour votre enregistrement DNS.

Ce site permet de vérifier que le champ DKIM de votre domaine est correct : http://dkimcore.org/c/keycheck Il faut parfois attendre plusieurs heures/jours que se propagent les modifications DNS.

Une fois l’enregistrement réalisé, il reste l’étape de signature des emails. Cette signature est composée d’un certain nombre de champs que nous allons voir ci-dessous.

Voyons étape par étape comment fonctionne la signature de l’email. Imaginons que vous souhaitez signer le mail suivant :

From: test@nageurs.com
To: toto@nowhere.com
Mailer: sans importance
Subject: bonjour de nageurs.com 

Bonjour Toto,
Si tu aimes la natation, inscris toi sur nageurs.com !

A+

Pour l’exemple on va choisir de signer en utilisant l’algorithme de cryptage rsa, et la fonction de hashage sha1.

La première étape est d’appliquer la fonction de hashage sur le contenu de l’email. Tous les retours à la ligne éventuels à la fin du contenu sont supprimés, et saut de ligne sont les deux caractères \r\n, ainsi la taille totale du contenu de l’email ci-dessus est de 75 caractères.

On applique la fonction SHA1 puis on encore le text en base64. Cela donne la chaîne suivante :

8FSexY8XmsBEh/+aIkM3hzA104I=

On isole ensuite les champs d’entête que l’on veut utiliser pour la signature, par exemple « From », « To », et « Subject » avec le début du champ DKIM-Signature. Si on veut signer un champ qui n’existe pas dans le mail d’origine on l’ajoute pour cette étape de signature. Ainsi nous allons signer toute la chaîne suivante :

From: test@nageurs.com
To: toto@nowhere.com
Subject: bonjour de nageurs.com
Content-type:
DKIM-Signature: v=1; a=rsa-sha1; q=dns/txt; l=75; s=dkim;
……..c=simple/simple;
……..h=From:To:Subject:Content-type;
……..d=nageurs.com; i=@nageurs.com;
……..bh=8FSexY8XmsBEh/+aIkM3hzA104I=;
……..b=

Quelques explications sur le champ DKIM-Signature ajouté ci-dessus. Le champ « c » (ici simple/simple) correspond au couple d’algorithmes de concaténation à utliser, la première valeur pour les en-têtes afin de générer le champ « b », le second pour le contenu du mail qui permettra de générer le champ « bh ».

 

Le champ « bh » est généré le premier à partir d’un hash sur le contenu du mail. La fonction de hash est définie par le champ « a », ici « a=rsa-sha1 ». Les entêtes du champ « h » sont celles isolées ci-dessus, juste avant le début du champ « DKIM-Signature » qui se termine pour le moment par « b= ».

 

La chaîne ci-dessus est ensuite encryptée avec la clé privée, et passée en base64. Cela donne la chaîne :

VZbTjEgzUo8z0Bgf7wXQcnvRIoz78U8Eov7gsiZho4HJHzr2Pvh97k+3/0AUL+M
wOdu6KS5cdu/q5JrFyOZtCiurl8VIx2jAvsXQsLvIsUlAbPbMWewx827Va1DD2dqj
ENDJHo29xJDm18PIBmkrUmkZ1xxcTGjf68hGEuyTTBg=

Il suffit de rajouter cette chaîne en face du champ « b= » et assembler le mail complet avec les entêtes d’origine. Cela donne l’email suivant :

From: test@nageurs.com
To: toto@nowhere.com
Mailer: sans importance
Subject: bonjour de nageurs.com
DKIM-Signature: v=1; a=rsa-sha1; q=dns/txt; l=75; s=dkim;
……..c=simple/simple;
……..h=From:To:Subject:Content-type;
……..d=nageurs.com; i=@nageurs.com;
……..bh=8FSexY8XmsBEh/+aIkM3hzA104I=;
……..b=VZbTjEgzUo8z0Bgf7wXQcnvRIoz78U8Eov7gsiZho4HJHzr2Pvh97k+3/0AUL+MwO
……..du6KS5cdu/q5JrFyOZtCiurl8VIx2jAvsXQsLvIsUlAbPbMWewx827Va1DD2dqjENDJHo
……..29xJDm18PIBmkrUmkZ1xxcTGjf68hGEuyTTBg=
 

Bonjour Toto,
Si tu aimes la natation, inscris toi sur nageurs.com !

A+

Pour tester que cela fonctionne vous pouvez bien sûr vous envoyer un email. Il y a auss des outils en ligne permettent ensuite de tester que le mail créé a un entête DKIM-Signature valide : https://9vx.org/~dho/dkim_validate.php

Comme prévu, quelques jours plus tard, la page Google+ apparaît à droite des emails !

Et voilà maintenant vous savez comment signer un email 🙂

N’hésitez pas à faire un lien vers cet article ! Merci 🙂

19 réflexions sur « Comment signer ses emails avec la norme DKIM ? »

    1. Merci ! Depuis que je m’ai commencé cette expérimentation je m’amuse à regarder parmi les newsletters que je reçois celles qui sont signées, parfois des grands sites négligent cette partie, dommage (pour eux) à mon avis !
      Bref autrement c’est plus pour le défi qu’autre chose dans mon cas, en effet pour l’instant la page google+ en question n’a eu qu’un seul nouveau ‘like’ !
      Amicalement,
      Christophe

  1. Hello boss
    habituellement tu as la volonté de vulgarisation cette fois tu restes avec tes amis chinois à parler chinois.. vais demander à ton employeur Serge de te payer une formation du genre « mon auditoire a t-il compris quelque chose » 🙂

    ne suis pas certain que ce message passe ai un message d’alerte en charabia aussi 🙂

  2. Bonjour Christophe,

    Pourrais tu me donner un coup de main pour installer DKIM sur mon site ?
    Je pense être tout proche de la solution mais je bute sur la clé privée, que je ne sais pas où sauver et sous quelle forme.
    De plus, est ce que les opérations de signature du mail sont réalisables en Php ?
    Car, dans ton tuto, tu vas vite et je ne suis pas tout.
    merci par avance pour ta réponse !

    Cordialement.

    Jean-Marc

    1. Bonjour,

      La clé privée sert à crypter les emails, il faut la stocker sur le serveur, tu l’utiliseras dans ton programme d’envoi de mail pour crypter la signature.
      Sinon oui ce mécanisme fonctionne en PHP et dans n’importe quel langage. Il y a probablement des librairies toutes prêtes, sinon ça peut se recoder, c’est ce que j’ai fait en perl car je voulais juste implémenter une partie de la RFC.

      Christophe

      Christophe

  3. Bonjour,

    Merci pour cette réponse.

    Mon problème était que je n’avais pas compris comment utiliser la clé privée et donc je butais sur le problème de son stockage.

    Je viens donc de comprendre que la clé privée était utilisée pour crypter les mails au départ et pouvait être utilisée par des logiciels tout faits qui donc, selon leur configuration allaient s’attendre à la trouver à un endroit déterminé, de façon tout à fait classique. Je pensais au départ que la clé privée devait pouvoir être accessible par les logiciels de messagerie des destinataires de mes mails pour décrypter les mails reçus. Mais ce n’est pas le cas, et heureusement car sinon le système n’aurait pas été fiable ! mais, voilà, n’y connaissant rien, je découvre !

    Donc, pour une utilisation comme celle que je veux en faire, c’est à dire codifier mes mails en PHP, il me faut juste utiliser cette clé dans le script PHP, inutile de chercher à la stocker où que ce soit en particulier.

    Il me reste maintenant à écrire le script PHP qui va utiliser le cryptage DKIM.

    Merci en tout cas pour ta réponse !

    Cordialement.

    Jean-Marc

  4. J’ai un mx qui est chez google(tata.com) et un mx sur mon serveur perso (toto.com)

    Je veux utiliser le serveur SMTP de toto.com sur mon serveur perso j’ai installé le dkim et le spf cela fonctionne en utilisant toto.com mais quand je lui dis à ce smtp que ca vient de from tata.com je n’ai pas la signature spf.

    Vous savez si il est possible de le faire signer du smtp de toto.com en passant un from de tata.com?

    Cordialement

  5. Bonjour,

    Je voulais me lancer dans le suivi du tutoriel, mais bloque à la première étape.

    Je n’arrive pas à générer les clés depuis l’invite de commandes (Windows 8) : « openssl n’est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes. »

    J’ai dû rater une évidence : la commande est-elle par exemple écrite pour Linux ?

    Merci pour ce partage.

    Salutations.

  6. @Mehdinefrance

    Effectivement c’est une commande linux.
    Dans le cas où vous n’auriez pas d’accès ssh à votre serveur, vous pouvez utiliser l’outil « Generate a DKIM Core Key » disponible au lien suivant :
    http://dkimcore.org/tools/

    Une fois vos clefs générées, pensez à demander la suppression de la page en cliquant sur le bouton en bas « Delete this page ».

    Salutations.

  7. @amlette

    Tout d’abord, un grand merci pour ce post.
    Je n’ai rien trouver de semblable sur la toile !

    J’implémente la norme DKIM en PHP…
    J’ai suivi les étapes une à une et tout est fonctionnel… Du moins presque.
    J’ai un serveur dédié chez 1&1 IONOS.
    J’ai utilisé PuTTY pour générer les clés (privé et public).
    J’ai ajouté la zone DKIM aux DNS de serveur :
    – Type : TXT
    – NOM D’HÔTE : dkim._domainkey
    – VALEUR : k=rsa;p=MaClePublicSurUneSeuleLigneSansRetourALaLigne
    J’ai contrôlé avec https://dkimcore.org/tools/keycheck.html
    – Selector : dkim
    – Domaine name : mondomaine.fr
    Tout est fonctionnel.
    J’ai encodé le message d’exemple de ce tuto ainsi (sans oublier de passer en BINAIRE avec l’option true pour le hashage…)
    $bh=base64_encode(sha1(‘Bonjour Toto,\r\nSi tu aimes la natation, inscris toi sur nageurs.com !\r\n\r\nA+’,true));
    J’obtiens bien la chaîne attendue : 8FSexY8XmsBEh/+aIkM3hzA104I=

    Et là, je bloque : Je ne comprend pas de quelle chaîne on parle ainsi « La chaîne ci-dessus est ensuite encryptée avec la clé privée, et passée en base64… »

    Quelle chaîne exactement ? Quel type d’encryptage avec clé privée ? Est-ce un openssl_private_encrypt puis un base64_encode ?

    Merci d’avance pour votre aide.

    Salutations,

    1. Bonjour @sichuila,

      La chaine à encoder avec votre clé privée est tout le bloc comprenant les entêtes (uniquement celles que vous mentionnez dans le champ « h » de DKIM-Signature), incluant l’entête DKIM-Signature et le corps du mail. L’algo de cryptage est spécifié dans le champ « a » donc ici c’est du RSA mais on peut utiliser autre chose.

      Cordialement,
      Christophe

  8. Bonjour

    J’ai longuement cherché une méthode pour intégrer manuellement la signature DKIM et je suis tombé sur ce forum qui traite précisément de ce cas de figure.

    Cependant, tout comme sichuila je ne parviens pas à finaliser la signature car la fin est quelque peu flou et j’espère que vous saurez m’aider.

    Exemple :
    Domaine : domaineperso.com
    Destinataire : destinataire@mail.com

    Code PHP :

    $sujet = « Message de test »;
    $message = ‘Message de testMessage’;
    $messageHash = sha1($message, true);
    $messageCrypte = base64_encode($messageHash);

    $headers[] = ‘MIME-Version: 1.0’;
    $headers[] = ‘Content-type: text/html; charset=utf-8’;
    $headers[] = ‘To: Nom destinataire’;
    $headers[] = ‘From: Domaine Perso ‘;
    $headers[] = ‘Subject: ‘.$sujet;
    $headers[] = ‘Content-type:
    DKIM-Signature: v=1; a=rsa-sha1; q=dns/txt; l=75; s=dkim;
    c=simple/simple;
    h=From:To:Subject:Content-type;
    d=domaineperso.com; i=@domaineperso.com;
    bh=’.$messageCrypte.’;
    b=’;

    À partir de là, je ne comprend pas comment générer « b ». Si j’ai bien compris, il faut exploiter la méthode openssl_private_encrypt($donnee, $variableResultat, $clePrive), mais je ne sais pas quoi transmettre pour la variable « $donnee ».

    Est-ce qu’il s’agit une nouvelle fois du contenu comme pour $messageHash ? Ou faut-il concaténer $message avec les $headers ?

    Si vous pouviez compléter mon exemple ce serait simplement parfait.

    Je vous remercie d’avance pour votre aide.

    1. Bonjour,
      Pour obtenir $b, il faut construire une chaîne qui est ensuite signée avec la clé privée puis encodée en base 64.
      La chaine en question est la concaténation des champs d’entête mentionnés dans le champ « h » et de l’entête « DKIM-Signature » complète sans la valeur de b. Comme précisé dans le post 🙂

      Bon courage !
      Christophe

  9. Bonjour Christophe

    J’ai longuement travaillé à l’intégration des signatures DKIM et je sens que je ne suis pas loin, seulement je ne sais pas pourquoi cela ne veut toujours pas fonctionner.

    Toute la partie clé publique me semble être bon :
    $_sujet = ‘Sujet du message’;
    $_messageHTML = ‘Votre message’;
    $_nomDestinataire = ‘Nom du destinataire’;
    $_destinataire = ‘mail@dudestinataire.com’;

    // Générer le message crypté
    $messageHash = sha1($_messageHTML, true);
    $messageCrypte = base64_encode($messageHash);

    // Générer le header
    $headers[] = ‘MIME-Version: 1.0’;
    $headers[] = ‘To: ‘.$_nomDestinataire. »;
    $headers[] = ‘From: MonSite ‘;
    $headers[] = ‘Subject: ‘.$_sujet;
    $headers[] = ‘Content-type: text/html; charset=utf-8’;
    $headers[] = ‘DKIM-Signature: v=1; a=rsa-sha1; q=dns/txt; l=75; s=dkim;
    c=simple/simple;
    h=From:To:Subject:Content-type;
    d=monsite.com; i=@monsite.com;
    bh=’.$messageCrypte.’;
    b=;’;

    J’ai cependant un doute sur le placement du content-type, car les messages que je reçois affiche tous la zone DKIM-Signature. Dans le même temps, même en déplaçant le content-type cela ne fonctionne pas d’avantage.

    Ensuite je finis avec l’encryptage avec le clé privée :

    // Encryption avec la clé privée
    $clePrivee = ‘Votre clé privée’;
    $pk = openssl_get_privatekey($clePrivee);

    // Définir le contenu qui doit être encrypté avec la clé privée
    $contenuPourClePrivee = $headers[1].$headers[2].$headers[3].$headers[4].$headers[5].$_messageHTML;

    // Décomposer le contenu de la clé privée en chaîne de 117 caractères pour permettre l’encryptage
    $listeChaine = str_split($contenuPourClePrivee, 117);
    $contenuClePrivee =  »;
    foreach($listeChaine as $c)
    {
    $encryptionPartielle =  »;
    $encryptionOk = openssl_private_encrypt($c, $encryptionPartielle, $pk, OPENSSL_PKCS1_PADDING);
    if($encryptionOk === false)
    return false;
    $contenuClePrivee .= $encryptionPartielle;
    }
    $b = base64_encode($contenuClePrivee);

    // Compléter le header avec la signature DKIM privée
    $headers[4] .= $b.’;’;

    // Expédition du mail
    mail($_destinataire, $_sujet, $_messageHTML, implode(« \r\n », $headers));

    J’ai l’impression d’avoir fait toutes les étapes décrient dans ce post, malgré tout les messages expédiés restent non signé.

    Sauriez-vous me dire où se trouve mon (mes) erreur(s) ?

    Merci pour votre aide.

  10. Merci pour votre aide Christophe.

    Malheureusement, je ne parviens pas à authentifier les mails avec DKIM malgré les explications. Je comprend que ce forum n’est pas dédié aux problèmes de développement et je n’insisterai pas d’avantage.

    Bonne continuation !

Laisser un commentaire