Certificats autosignés

Certificats autosignés

Aujourd’hui on va parler de certificats autosignés et surtout comment les mettre en place sur vos machines de développement sans faire ça comme des sagouins.

Cet article est le premier de ce blog à avoir une vocation pratique et technique, aussi il sera un peu différent des précédents avec notamment un format plus long (j’ai besoin de mon espace de blabla et de mon espace de démonstration) et des extraits de code qui viennent remplacer les images “décoratives” quand on sera dans la partie pratique. L’article d’aujourd’hui ne s’y prête pas forcément mais on aura sans doute aussi de nombreux screenshots dans nos futurs articles de ce type.

Je précise également à toutes fins utiles que l’essentiel des exemples qui seront décrits au fil de cet article et de ce blog en général prendront place en environnement Linux. Pour ceux qui veulent faire ça sous Windows, le principe reste le même mais je vous laisse chercher les outils et commandes équivalents pour cet OS.

Notez que je ne suis pas fermé à l’utilisation de Windows pour les développeurs – et il existe d’ailleurs des tas de bonnes raisons de le faire selon le type de projet auquel vous vous attelez.

Encore nos histoires de signature

Dans le cadre de l’article précédent sur les certificats, on a évoqué comment ils permettent à un individu ou une machine d’assurer son identité lors d’échanges en réseau et comment une confiance est apportée ou non par la machine cliente (par le biais de votre navigateur dans le cas d’une page web) en fonction de comment et par qui ce certificat a été signé.

Il est possible pour un certificat d’être auto-signé, c’est à dire qu’aucun CA tiers n’a donné son tampon d’authenticité à ce dernier. Cela s’avère très utile lorsque vous êtes en phase de développement car cela permet d’avoir du HTTPS “rapide et gratuit” (on reviendra sur l’aspect gratuit dans un article futur). Néanmoins, pour des raisons évidentes de sécurité, les certificats autosignés vont systématiquement lever des alertes pour les applications tierces qui tombent dessus (application cliente, navigateur web, etc.), sauf si ces dernières n’ont elle-même aucun dispositif de sécurité mis en place pour s’en prémunir (et dans ce cas, foutez ça à la poubelle et prenez quelque chose de mieux, ça vous évitera des ennuis croyez-moi).

Développer en HTTPS

Lorsque vous devez développer une application web basée sur le protocole HTTPS, plusieurs chemins s’offrent à vous. Certains ont l’avantage d’être rapides, d’autres ont celui de se rapprocher le plus possible de la réalité que vous aurez en prod (on appelle prod ou production l’environnement où tourne l’instance de l’application qui est vraiment exposée aux utilisateurs) mais tous n’ont pas le même niveau de sécurisation.

La plupart du temps, vous opterez pour l’une des voies suivantes :

  • tout faire en HTTP sur votre environnement de développement (qu’on désignera dev par la suite) et passer en HTTPS pour la production
  • utiliser des certificats autosignés pour vos développements
  • utiliser de vrais certificats – émis par une CA reconnue – pour votre machine de développement

Je déconseille la première option pour plein de raisons. Certes c’est la plus rapide à mettre en place (vous pouvez vous lancer tête baissée dans vos développements), mais vous allez vous arracher les cheveux au moment du passage en HTTPS. Sous ce S supplémentaire se cachent beaucoup de contraintes et d’effets de bord (une conséquence indirecte de quelque chose que vous mettez en place dans votre code) et croyez-moi, vous ne pourrez pas tous les anticiper.

La troisième option ne se justifie généralement que dans des cas très spécifiques où le niveau de sécurité de l’application cible est maximale (pensez données médicales ou de type confidentiel défense par exemple) et pour lesquelles on va vous demander de vous interfacer dès les développements avec des machines elles-mêmes très sécurisées. Dans ce cas on vous fournira d’ailleurs sûrement au passage ledit certificat parce qu’on ne fait même plus confiance aux CA habituels, on veut du top moumoute !

La deuxième option est ainsi selon moi la plus viable dans les cas d’usage courant et donc celle avec laquelle il faudra que vous soyez à l’aise. C’est donc après ce gros pavé que vous comprenez le pourquoi du pour de l’existence de cet article.

L’art de l’autosignature

les problèmes

Utiliser des certificats autosignés peut poser quelques problèmes : en effet, comme précisé plus haut, les applications clientes ne vous feront de base pas confiance. Vous ajoutez donc bien la couche de chiffrement mais par contre l’authenticité de votre identité est clairement au niveau 0 et c’est tant mieux. Bah oui parce que sinon vous pourriez autosigner un certificat pour le domaine google.com et après c’est fiesta, le monde vous appartient pour lancer du gros man in the middle !

Si ce comportement est souhaitable, il va néanmoins poser des problèmes. Prenons un exemple concret : vous développez une application A et une application B qui devront communiquer entre-elles sur la base d’un protocole HTTPS. Vous créez deux certificats autosignés, un pour A, un pour B. Vous ajoutez une fonction de test de chaque côté pour vérifier que chacun peut bien communiquer avec le voisin, 30 minutes au four en mode grill à 220° sans préchauffage et… rien ne marche !

Si vous avez correctement fait vos prototypes pour A et B, chacune des applications, en recevant une communication de l’autre, devrait refuser de traiter la requête avec une erreur du type : “self signed certificate in certificate chain”. Vous vous mangerez la même porte avec votre navigateur si l’une ou l’autre des applications dispose d’un frontend (partie d’une application web exposée à l’utilisateur : typiquement une page web).

la solution des sagouins

Pour faire face au problème ci-dessus, certains iront droit au but par manque de temps (c’est sale mais ça arrive), ou de connaissances. On va alors demander à notre navigateur de faire une exception sur ce nom de domaine (faille de sécurité à la clé si vous oubliez d’enlever cette exception après coup) ou encore ajouter dans nos requêtes des aberrations du type “rejectUnauthorized: false”, autrement dit : si la réponse reçue pue le faux, accepte-la quand même.

Autre solution un peu plus subtile mais quand même bien sale va être de créer un script intermédiaire pour lancer votre application, par exemple dev-run-A.sh, qui lève les sécurités via des variables d’environnement (on reparlera de ces variables une autre fois) avant de lancer A (et idem pour B). Vous savez alors que ce fichier n’est à exécuter qu’en environnement de développement (notez que cette information est transparente dans le nom du fichier, ce qui est une bonne habitude à prendre). Là on est sur un truc un peu moins porcasse mais c’est quand même peu élégant car vous dissociez le flux d’exécution de la dev et de la prod.

la solution élégante

La solution la plus propre pour faire vos développements en autosigné va être de monter un cran dans la chaîne de certification : on va se créer un CA rien qu’à nous.

Il est en effet possible – et, dans notre cas présent, souhaitable – de créer un certificat de type CA, qui nous permettra donc de signer nos autres certificats plutôt que de les laisser autosignés. “Oui mais du coup c’est au CA que nos applications clientes ne vont pas faire confiance, c’est le même problème non ?” Oui et non : on va ajouter notre CA dans la liste des CA de confiance, à la fois côté machine qui héberge les applications – ce qui permettra à A de faire confiance au CA qui a signé le certificat de B et vice-versa – et dans notre navigateur afin qu’il fasse confiance à tout certificat que notre CA a signé.

“T’es en train de nous douiller là non ? On retombe sur un fonctionnement à base d’exception, c’est pas mieux qu’avant !” Pas tout à fait !

Déjà, côté navigateur : si vous mettez une exception pour le site A parce que vous utilisez un autosigné, cette exception est absolue : tout problème de sécurité qui a lieu tant que l’exception existe sera ignoré, que cela soit celui que vous avez anticipé ou un autre. Vous perdez donc en capacité de détection des bugs et en sécurité.

Dans le cas d’un CA maison ajouté dans votre navigateur, vous n’avez pas ce problème puisque votre navigateur va respecter son comportement habituel : si vous avez un problème d’une autre nature lié à vos certificats (par exemple ils sont mal positionnés dans vos applications ou quelqu’un essaye de vous faire un man in the middle en ayant autosigné un truc de son côté) il saura vous le dire.

Côté CA ajouté dans la liste de confiance de votre machine : même combat ! Votre machine fera comme elle l’a toujours fait, vous admettez juste un nouveau CA de confiance.

Les retombées sont par contre très bénéfiques puisque vous n’avez plus besoin de script de lancement spécifique à la machine de dev (hourra) et encore moins de bouts de code qui réduisent au silence les vérifications de sécurité.

Cerise sur le gâteau : vous pouvez utiliser le CA maison que vous créez pour signer tous vos futurs certificats. Cette modification unique et sans danger (tant que vous gardez votre CA sécurisé et en lieu sûr) vous assure un fonctionnement transparent pour tous vos futurs besoins.

“Mais c’est trop bien, on signe où ?” (ce jeu de mot pourri n’est pas accidentel). Pour le cas pratique, c’est juste en dessous. Vous allez voir c’est très simple.

Dans la pratique

OpenSSL : l’outil de la situation

OpenSSL est une boîte à outils cryptographiques 100% libre et d’excellente qualité. Elle permet entre autres de générer des couples clé publique / clé privée, et donc aussi des certificats.

L’outil est très complet et permet donc des utilisations très complexes qui dépassent de loin mes connaissances et notre besoin présent. Je ne vais donc pas rentrer dans le détail de tout ce qu’il peut faire et on va se contenter des commandes qui nous intéressent dans l’immédiat.

Créer un CA maison

On va commencer par générer une clé privée avec openSSL :

openssl genrsa -aes128 -out prims-master-ca.key 4096

Petite explication sur les arguments de cette commande :

  • genrsa : on demande la génération d’une clé privée de type RSA (le chiffrement RSA est le chiffrement asymétrique le plus populaire et le plus utilisé sur le web ; il est nommé d’après les initiales de ses créateurs : Rivest, Shamir et Adleman)
  • -aes128 : chiffrement utilisé pour encrypter la clé elle-même
  • -out <fichier> : fichier de sortie où stocker la clé privée
  • 4096 : taille de la clé privée générée

Lors de l’utilisation de la commande, on va vous demander un mot de passe (utilisez un mdp fort et unique pour une sécurité maximale). Retenez bien ce dernier car il vous sera demandé pour chaque signature à effectuer avec notre CA maison.

Une fois la clé privée en main, on peut générer le certificat à proprement parler :

openssl req -x509 -key prims-master-ca.key -days 10000 -out prims-master-ca.crt

Ici, les arguments sont :

  • req : on demande la création d’une requête de certification ou d’un certificat PKCS#10 (standard pour un CA)
  • -x590 : plus précisément on veut générer un certificat
  • -key <fichier> : fichier qui contient la clé privée de l’étape précédente
  • -days <nombre> : nombre de jours de validité du certificat (ici je mets 10000 pour être tranquille)
  • -out <fichier> : fichier de sortie où stocker le certificat

Des questions nous sont posées, libre à vous d’y répondre ou de laisser les valeurs par défaut pour votre CA maison. Sachez que ces informations sont publiquement consultables lorsqu’on regarde la chaîne de certification d’une application ou d’un site web.

Faire confiance à son CA

Cas du navigateur

Pour ajouter un CA de confiance à son navigateur, c’est généralement assez simple. Pour Firefox, vous trouverez dans la section “Sécurité > certificats”, un bouton “Afficher les certificats” qui va vous ouvrir un gestionnaire.

De là, vous n’avez plus qu’à importer votre fichier .crt généré plus haut.

La plupart des autres navigateurs courants (Chrome, Safari, etc.) disposent d’une fonctionnalité similaire. Une recherche rapide dans vos paramètres – ou, à défaut, dans votre moteur de recherche préféré – devrait rapidement pouvoir porter ses fruits.

Cas du système

Pour que votre machine de dev reconnaisse votre CA et exécute vos applications avec cette confiance toute nouvelle, vous devez l’ajouter dans un dossier précis et lancer une commande précise. Ces deux éléments sont variables en fonction de votre OS.

Pour les OS Debian/Ubuntu, il faut déposer votre fichier .crt dans /usr/local/share/ca-certificates/ ou un de ses sous-dossiers. Si ce dossier n’existe pas, il faut le créer et lui donner les permissions 775 :

mkdir -p /usr/local/share/ca-certificates/
chmod 755 /usr/local/share/ca-certificates

Une fois votre fichier .crt déposé dans cette arborescence, petite commande de mise à jour :

update-ca-certificates

Pour les distributions CentOS, vous devrez déposer votre fichier dans /etc/pki/ca-trust/source/anchors/ ou un sous-dossier puis lancer la commande “update-ca-trust”.

Pour les autres distributions, un petit coup d’œil dans votre moteur de recherche et vous devriez vous en sortir.

Note importante : les certificats peuvent être déposés à la racine du dossier approprié ou dans un sous dossier. Certaines personnes préfèrent des sous-dossiers pour organiser tout ça un peu plus proprement, toutefois je vous conseille au contraire de tout mettre à la racine (par exemple /usr/local/share/ca-certificates/).

En effet, la plupart des systèmes gardent une base de données des CA de confiance qu’ils constituent sur la base du nom du fichier, mais pas de son chemin d’accès. Du coup si vous avez A/ca.crt et B/ca.crt dans ca-certificates/, un seul des deux sera pris en compte. Avoir tout dans le même dossier facilite donc la détection en amont de ce genre de problème. De plus, il y a peu de chances que vous ayez à jongler avec une multitude de CA maison sur votre dev alors à quoi bon les organiser en sous-dossiers ?

Signer un certificat

Maintenant qu’on a un CA à nous et que notre machine et notre navigateur sont prêts à lui faire confiance, on n’a plus qu’à générer les certificats pour nos hôtes et les signer avec pour que tout soit prêt.

On commence par se créer une clé privée :

openssl genrsa -out exemple.key 4096

Vous remarquerez ici que je ne demande pas d’encrypter la clé et qu’en conséquence on ne me demande pas de mot de passe.

On génère ensuite une requête de signature (fichier .csr) avec la commande suivante :

openssl req -new -key exemple.key -out exemple.csr

Là on nous pose les mêmes questions qu’au moment de la génération du certificat CA. Encore une fois, libre à vous d’y répondre ou de laisser les valeurs par défaut. Attention quand même : il est préférable pour la question Common Name de mettre le FQDN (Fully Qualified Domain Name, on y reviendra) de votre serveur car il peut y avoir des contrôles sur ce champ. Mettez donc localhost ici (ou autre si vous avez un host particulier).

Et maintenant on va pouvoir générer un fichier de configuration pour la signature du certificat. Il faut pour cela créer un fichier .ext (appelons le exemple.ext pour notre exemple) et y mettre les éléments suivants :

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost

Dans le détail :

  • authorityKeyIdentifier=keyid,issuer : l’identification de la clé publique du certificat parent (notre CA) est copiée dans le certificat enfant (celui qu’on s’apprête à signer) ; à défaut on copiera le nom de l’autorité émettrice et son numéro de série
  • basicConstraints=CA:FALSE : le certificat qu’on va signer n’est pas un CA
  • keyUsage : liste des utilisations qu’on certifie pour le certificat enfant ; ici on peut signer les données et la signature est irrévocable (non-répudiation) et on peut encrypter des clés cryptographique (typiquement pour sécuriser le partage d’une clé de chiffrement symétrique) ou de la donnée lambda
  • subjectAltName = @alt_names : on signale qu’une liste alt_names sera fournie pour les noms du “sujet” du certificat (autrement dit qui on certifie)
  • [alt_names] : liste des noms de sujet admis (voir ci-dessous) ; ici on met localhost par exemple pour une application qui tournera localement sur notre dev

Les alt_names sont toutes les identités que le CA va certifier en signant le certificat. Par exemple, on pourrait imaginer :

DNS.1 exemple.fr
DNS.2 blog.exemple.fr
DNS.3 forum.exemple.fr

Dès lors, une requête en provenance de ces trois FQDN (encore une fois, on y reviendra) est correctement signée si elle est associée à notre certificat. Par contre, on aurait une erreur si “boutique.exemple.fr” essayait d’utiliser ce certificat pour signer une requête.

Note importante : dans le cadre de développements avec plusieurs machines en réseau ou plusieurs VM, vous pourrez avoir besoin de certifier une machine non pas sur la base d’un nom de domaine, mais plutôt sur la base d’une IP. C’est tout à fait possible mais il faut alors dans les alt_names avoir une ligne de la forme IP.n <IP>, par exemple localement :

IP.1 127.0.0.1

Une fois notre fichier .ext prêt, on va pouvoir procéder à la signature à proprement parler. Pour ce faire, on a une nouvelle fois recours à openSSL :

openssl x509 -req -in exemple.csr -CA prims-master-ca.crt -CAkey prims-master-ca.key -CAcreateserial -out exemple.crt -days 365 -extfile exemple.ext

Ici on a :

  • x509 : on souhaite afficher ou signer un certificat
  • -req : le csr d’entrée doit être un PKCS#10 (standard pour une demande de signature par un CA)
  • -in <fichier> : chemin du fichier csr créé précédemment
  • -CA <fichier> : chemin du fichier crt de notre CA
  • – CAkey <fichier> : chemin du fichier key de notre CA
  • – out <fichier> : fichier de sortie où stocker notre certificat
  • -days <nombre> : nombre de jours de validité du certificat
  • -extfile <fichier> : chemin du fichier .ext précédemment créé

Il vous sera demandé le mot de passe de la clé de votre CA et le tour est joué : vous venez de signer un certificat hôte.

Dès lors, il sera considéré par votre machine et votre navigateur que l’autorité de certification qui l’a signée est légitime et vous n’aurez plus les vilaines erreurs et les besoins d’exception que vous auriez eu autrement. Attention toutefois : vous pourrez toujours avoir des erreurs si vous vous en servez mal (par exemple en utilisant le certificat avec une IP qui n’était pas listée dans vos alt_names), mais c’est tant mieux, car ce sont les mêmes erreurs que vous auriez eu en prod avec les “vrais” certificats.

Conclusions

Travailler proprement avec les certificats en environnement de développement n’est pas si compliqué : il suffit de se créer un certificat CA maison et de l’ajouter à ceux auxquels votre machine et votre navigateur font confiance.

Cette manipulation est rapide et peut dans la plupart des cas être faite une unique fois pour tous vos développements futurs (sauf si vous voulez encore plus de sécurité et que vous mettez une faible durée de validité pour votre CA).

Une fois que c’est fait ? Vous êtes maître de la situation pour créer et certifier autant de certificats enfants que vous le souhaitez : les erreurs et alertes sont encore possibles mais il y a fort à parier que vous auriez eu les même sur votre prod et c’est donc une très bonne chose qu’elles se manifestent en dev !


3 responses to “Certificats autosignés”

  1. […] résumé : idéalement ne mettez jamais en place d’exception (même pour localhost – un article à paraître bientôt vous expliquera comment faire ça proprement et simplement pour vos développements locaux) mais si […]

  2. […] vous invite à faire les manipulations décrites dans l’article sur les certificats autosignés, à regarder les droits qui sont donnés aux fichiers de sortie par openssl et à vous demander […]

  3. […] vous vous souvenez aussi qu’on peut créer sa propre autorité de certification pour signer ses propres certificats. Et ça, JoeWesh le sait bien lui aussi. Il a d’ailleurs créé une fausse version du site de […]

Leave a Reply

Your email address will not be published.