Certificat x509 pour votre Apache
Vous souvenez vous de l'article de GanGan sur les certificats x509 ? Le bien nommé Certificats ssl pour du https vite fait mal fait :p ? Bien que ne faisant pas dans la dentelle et allant au plus efficace, il n'avait de mauvais que le manque d'explication sur OpenSSL.
L'article ci-après rattrape un peu cet oubli.
Le principal reproche que l'on peut lui faire concerne la génération d'un certificat x509 auto-signé pour sécuriser un serveur web qui rend caduque les principaux aspects de l'utilisation de SSL/TLS et ici nous allons utiliser une technique plus propre.
L'objectif n'est pas de maîtriser toutes les subtilités de ce protocole (ce qui pourrait prendre plusieurs semaines) mais il y a quelques points essentiels à connaître :
- C'est un protocole de couche session destiné à protéger et encapsuler n'importe quel autre protocole. Aussi bien les classiques HTTP et FTP, que les plus exotiques possibles : IMAP, POP, SMTP, SIP, RTP, XMPP, IRC .... Il peut même être utilisé pour transporter une seconde couche IP dans le cadre des VPNs.
- Des certificats x509, en vrai des clés asymétriques, sont nécessaires. Pour plus de détails sur ces clés, je vous renvoie vers l'excellente et exhaustive explication de Christian Caleca ou vers mon modeste article sur GnuPG qui traitait déjà un peu cet aspect.
- SSL/TLS repose sur 4 principes : chiffrement, authentification, non-répudiation et intégrité des données. Le Chiffrement c'est bien sûr le fait de rendre illisible au yeux des mortels le contenu d'un message, l'authentification c'est l'idée que les deux parties communicantes se reconnaissent mutuellement, la non-répudiation est son pendant : ne pas pouvoir renier ce que l'on a émis; et l'intégrité est l'assurance que les données ne peuvent être falsifiées au moment de la transaction.
- L'utilisation d'un tiers de confiance est primordiale. Un tiers de confiance est l'équivalent d'un notaire -un peu suisse sur les bords- qui vérifie les identités des deux autres protagonistes (c'est pour ça que l'on parle de "tiers"), signe les cartes d'identités (les fameux certificats) et se rend donc responsable en cas de pépin. C'est lui qui assure l'authenticité et la non-répudiation. Dans la pratique, il existe de nombreux tiers de confiance : VeriSign, Thawte, CACert .... Ce sont des Autorités de Certifications (CA).
- Dernier point : le manuel OpenSSL est grosso-modo aussi touffu que le seigneur des anneaux, les trolls en moins, mais les deux lectures sont tout autant constructive et passionnante ! ^^
Après cette mise au point, on ressent vite les manques de la méthode rapide de Gangan : pas de tiers de confiance, authentification faible, non-répudiation impossible et je ne parle même pas des alertes du navigateur en se connectant sur le serveur web
La solution que je propose ci-après est l'utilisation d'une autorité de certification indépendante nommé CACert. Il faut savoir que tous les navigateurs contiennent ce que l'on nomme une bibliothèque de certificats. Cette bibliothèque contient les clés publiques de nombreux tiers de confiance "officiels". Ces listes sont définies arbitrairement depuis des années selon l'idée qu'une entreprise ayant une existence palpable peut faire office de tiers de confiance et non que la confiance peut se gagner sur la réputation et la qualité du service plutôt que l'argent. L'autorité CACert par exemple n'est pas installé par défaut dans un Firefox sous Windows et si un site utilise un certificat signé par cet organisme alors le butineur va doucement paniquer. Il suffira d'ajouter les certificats racines officiels de CACert dans la bibliothèque et il sera rassuré définitivement pour tous les certificats issues de cette autorité. Sur mes Debian elle est installée par défaut dans IceWeasel par contre.
Enfin le débat n'est pas de savoir qui devrait ou non être dans la bibliothèque. CACert permet d'obtenir gratuitement de beaux certificats signés par une véritable CA et c'est juste ça mon besoin. Mais je palabre, je palabre et je m'égare.
1 / Requête d'un certificat.
On va débuter le travail en générant une requête de certificat x509. En l'état ce sera en fait un semblant de certificat mais non-signé et inexploitable.
Openssl s'utilise de cette manière : le premier paramètre est le type de fichier/chiffrement sur lequel on va travailler et ensuite ce sont les actions à effectuer dessus. Ici on demande à openssl de créer (-new) une requête (req), d'écrire la clé privé dans hinau.lt.pem (-keyout) et d'écrire la sortie standard dans hinau.lt.csr (-out) :
$ openssl req -new -keyout hinau.lt.pem -out hinau.lt.csr Generating a 1024 bit RSA private key ...........................++++++ ...++++++ writing new private key to 'hinau.lt.pem' Enter PEM pass phrase:********* Verifying - Enter PEM pass phrase:********* ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:FR State or Province Name (full name) [Some-State]:__France Locality Name (eg, city) []:__Lannion Organization Name (eg, company) [Internet Widgits Pty Ltd]:No company Organizational Unit Name (eg, section) []:No section Common Name (eg, YOUR name) []:*.hinau.lt Email Address []:hinault@gmail.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Pour travailler sur un certificat x509, on aurait mis "openssl x509 < ..actions.. >".
Pour travailler avec diffie-hellmann : "openssl dh < ..actions.. >".
Pour transformer quelque chose en base64 : "openssl base64 < ..actions.. >".
Facile non ?
Par exemple pour extraire le contenu du fichier en mode texte on associe -noout et -text :
$ openssl req -in hinau.lt.csr -noout -text Certificate Request: Data: Version: 0 (0x0) Subject: C=FR, ST=France, L=Lannion, O=No company, OU=No section, CN=*.hinau.lt/emailAddress=hinault@gmail.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:c5:92:a2:72:88:21:cb:58:69:78:9d:d2:ec:20: f5:ee:f8:bf:58:25:94:59:e4:f6:c8:15:32:70:0b: 9e:8e:8d:aa:be:c7:f8:26:98:02:ef:94:de:91:aa: 69:55:1b:b0:35:de:d0:8e:05:f3:b8:f8:92:3e:b5: 61:66:42:dd:94:ed:b6:ca:a6:36:49:ff:e2:6c:32: 0e:e6:a3:2c:f4:68:86:9c:1b:a1:4e:01:6a:77:a8: 29:9a:c1:cc:50:b7:11:cf:a8:62:33:69:4a:c2:8f: d9:ce:65:ab:7e:4a:ee:68:bd:aa:54:c3:97:a5:9a: 1f:b7:c4:71:30:c1:55:05:31 Exponent: 65537 (0x10001) Attributes: a0:00 Signature Algorithm: sha1WithRSAEncryption 34:3c:8a:a6:d0:f2:b4:e5:92:1e:28:1f:3e:6e:4b:97:40:ee: c4:26:2b:77:0a:b0:7c:dc:db:04:70:c0:b9:eb:4c:7f:1b:8d: b5:dd:23:f4:e5:2c:0f:5e:44:0f:f2:c5:02:b0:91:31:cb:43: e3:7e:9f:61:a7:d5:c1:19:6d:a2:ab:72:b9:84:c0:0c:8f:ed: 29:27:1d:2b:97:04:b4:88:ab:bf:33:5a:8e:6d:48:0e:c5:4a: 36:8c:27:16:93:f2:dc:50:c8:db:49:c9:1b:fe:da:96:d5:cf: 67:c3:b1:cd:0a:1c:f9:67:2b:5e:e1:9f:47:41:9f:64:b3:09: f9:4d
Pour avoir seulement le subject :
$ openssl req -in hinau.lt.csr -noout -subject subject=/C=FR/ST=France/L=Lannion/O=No company/OU=No section/CN=*.hinau.lt/emailAddress=hinault@gmail.com
Pour l'aide sur les options de req :
$ openssl req help
(en fait help n'existe pas mais l'aide s'affiche en cas d'erreur :p)
Etc...
Vous avez remarqué que j'ai mis *.hinau.lt pour le champ Common Name. C'est en fait une parade à un problème imprévu dans la norme : l'ITU n'avait pas pensé que l'on pourrait avoir plusieurs certificats pour plusieurs sous-domaines.
Mr Stéphane «DNS Master» Bortzmeyer en parle mieux que moi mais pour être dans les clous, il faudrait un certificat par virtualhost avec tout ce que ça demande comme maintenance... l'étoile permet de faire accepter un même certificat pour tous les virtualhost mais attention ce n'est pas standard, votre navigateur ne le supportera peut être pas et ca ne fonctionne que pour les sous-domaines. Le domaine hinau.lt devra avoir son propre certificat.
2 / Signature du certificat
Maintenant que l'on a notre requête non signée, il va falloir demander à la CA d'y apposer sa griffe. CACert propose une interface web pour ça.
Pré-requis : Une fois votre inscription effectuée sur cacert.org, il faut ajouter votre domaine dans la liste que vous gérez. L'interface vous demandera ensuite de choisir une adresse officielle pour ce domaine. Dans mon cas, ce sera celle qui est dans le subject plus haut. Un mail contenant un lien est envoyé à cette adresse. Le lien vous ramènera sur le site de CACert et vous demandera de confirmer l'ajout du domaine. Ensuite vous pourrez demander une signature propre de la requête.
Copiez/collez le contenu de la requête ( hinau.lt.csr pour moi ) dans le champ de texte de la section "Certificats de Serveur".
$ cat hinau.lt.csr -----BEGIN CERTIFICATE REQUEST----- MIIB0jCCATsCAQAwgZExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxEDAO BgNVBAcTB0xhbm5pb24xEzARBgNVBAoTCk5vIGNvbXBhbnkxEzARBgNVBAsTCk5v IHNlY3Rpb24xEzARBgNVBAMUCiouaGluYXUubHQxIDAeBgkqhkiG9w0BCQEWEWhp bmF1bHRAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFkqJy iCHLWGl4ndLsIPXu+L9YJZRZ5PbIFTJwC56Ojaq+x/gmmALvlN6RqmlVG7A13tCO BfO4+JI+tWFmQt2U7bbKpjZJ/+JsMg7moyz0aIacG6FOAWp3qCmawcxQtxHPqGIz aUrCj9nOZat+Su5ovapUw5elmh+3xHEwwVUFMQIDAQABoAAwDQYJKoZIhvcNAQEF BQADgYEANDyKptDytOWSHigfPm5Ll0DuxCYrdwqwfNzbBHDAuetMfxuNtd0j9OUs D15ED/LFArCRMctD436fYafVwRltoqtyuYTADI/tKScdK5cEtIirvzNajm1IDsVK NownFpPy3FDI20nJG/7altXPZ8OxzQic+WcrXuGfR0GfZLMJ+U0= -----END CERTIFICATE REQUEST-----
Suite à une petite validation, cacert vous délivrera le certificat signé !
Encore une étape de copier/coller de ce certificat dans un fichier texte ( hinau.lt.crt pour moi ) et vous pourrez l'utiliser à votre convenance dans votre serveur web préféré.
Pour connaître le contenu de ce certificat, réutilisez la commande openssl cette fois en précisant le paramètre x509 :
$ openssl x509 -in hinau.lt.crt -noout -text Certificate: Data: Version: 3 (0x2) Serial Number: 531692 (0x81cec) Signature Algorithm: sha1WithRSAEncryption Issuer: O=Root CA, OU=http://www.cacert.org, CN=CA Cert Signing Authority/emailAddress=support@cacert.org Validity Not Before: Jan 26 15:17:54 2010 GMT Not After : Jul 25 15:17:54 2010 GMT Subject: CN=*.hinau.lt Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:c5:92:a2:72:88:21:cb:58:69:78:9d:d2:ec:20: f5:ee:f8:bf:58:25:94:59:e4:f6:c8:15:32:70:0b: 9e:8e:8d:aa:be:c7:f8:26:98:02:ef:94:de:91:aa: 69:55:1b:b0:35:de:d0:8e:05:f3:b8:f8:92:3e:b5: 61:66:42:dd:94:ed:b6:ca:a6:36:49:ff:e2:6c:32: 0e:e6:a3:2c:f4:68:86:9c:1b:a1:4e:01:6a:77:a8: 29:9a:c1:cc:50:b7:11:cf:a8:62:33:69:4a:c2:8f: d9:ce:65:ab:7e:4a:ee:68:bd:aa:54:c3:97:a5:9a: 1f:b7:c4:71:30:c1:55:05:31 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Extended Key Usage: TLS Web Client Authentication, TLS Web Server Authentication, Netscape Server Gated Crypto, Microsoft Server Gated Crypto X509v3 Key Usage: Digital Signature, Key Encipherment Authority Information Access: OCSP - URI:http://ocsp.cacert.org/ X509v3 Subject Alternative Name: DNS:*.hinau.lt, othername:<unsupported> Signature Algorithm: sha1WithRSAEncryption 33:7d:de:4c:f3:9b:39:d1:b3:e0:88:e8:51:a0:be:f0:43:02: 39:ad:38:e1:36:18:12:84:0d:dd:75:e2:cd:e2:7e:b1:d8:aa: 45:8a:a3:ca:ce:a2:b9:ac:51:70:5b:9b:60:46:1d:b7:a1:23: 46:26:84:72:1c:19:45:f3:3c:7b:18:21:75:e0:6d:cc:ad:74: d5:b3:32:0f:c1:2d:78:bd:5f:e8:89:37:71:f3:cd:0f:31:0c: 7b:17:c1:d1:e2:11:1f:cb:98:03:ff:2a:f8:55:51:c4:a7:f0: f1:7a:69:35:e3:6d:5a:d6:3f:af:c7:bc:06:55:f8:61:36:0f: 8b:ea:2d:6b:9a:3d:8c:4e:f4:af:bf:91:f5:f7:e4:a8:9c:22: 9a:52:b9:de:16:c8:8a:ad:3a:b5:fd:03:fd:1a:af:30:65:76: f3:93:86:21:57:80:57:90:39:27:ef:75:2e:83:50:25:71:74: c8:1f:82:a3:0a:ad:8a:e5:14:94:df:5e:13:38:0b:b9:12:cb: 3a:d9:fa:52:8b:c6:69:4c:ae:be:1a:d5:8e:a5:a4:c2:49:7e: d2:9c:b9:e0:f3:f4:4c:7e:43:44:f1:33:4d:80:71:2f:14:3b: 01:e2:f6:c7:75:5e:d7:cf:8b:c8:52:6e:a0:5b:14:cd:a2:67: 0e:34:f1:32:64:fb:84:95:12:c2:7a:d8:a9:f4:36:1e:51:27: ea:5f:56:29:e0:b1:bf:77:f4:25:f2:f3:1a:55:e4:80:49:1e: f2:3c:9c:fc:0f:35:4c:74:c8:a9:61:bf:fe:f1:be:03:82:7e: 18:69:bd:04:c7:4a:67:04:4b:97:7a:03:88:b6:ee:52:d0:2a: 90:c1:e9:3f:8a:77:6b:16:41:2f:aa:0a:ac:1e:db:a8:c7:e4: 07:b1:4b:8d:55:73:43:85:3d:97:6c:33:22:43:33:cd:3f:cb: f5:63:56:7f:ba:35:6b:58:94:84:15:b1:35:12:03:9e:ed:0c: d8:bf:32:c0:ef:1a:33:d4:c1:ad:57:5b:01:12:2d:57:c0:2f: 01:2b:53:9e:9e:c6:2c:4c:1f:9c:89:29:41:1c:0d:87:8d:3a: 7d:75:47:f6:d9:ca:7f:9b:c1:ba:59:56:98:66:ae:5c:15:44: fc:1b:15:5e:ce:45:22:2a:d9:d2:b5:e7:90:db:27:4b:d2:c7: 65:7a:cf:85:c1:9b:bc:92:47:4b:3a:b5:43:f0:55:8d:c7:48: 12:da:7c:06:5a:d1:cc:8c:3a:c4:4e:ef:2a:3e:99:fe:bc:25: 28:bc:a1:e0:a0:39:2b:65:4b:3c:89:9e:2d:bf:0d:86:da:6d: aa:09:66:51:7e:37:56:1b
Les nouveautés notables sont ici les champs Issuer et Validity. Le premier identifie la CA du certificat et la seconde la validité du certificat. Le reste aussi est très utile mais peu lisible et je ne rentrerais pas dans les détails pour cet article
Si vous vous demandez pourquoi je fournis aussi facilement les informations sur la signature c'est tout simplement parce que le fichier que j'ai fourni ( hinau.lt.csr ) ainsi que le certificat reçu en retour ( hinau.lt.crt ) représentent les certificats publiques ( respectivement avant et après signature mais c'est kif ). A ce titre, c'est exactement le même certificat que vous téléchargeriez en vous connectant sur https://kevin.hinau.lt ! Peu importe alors que je la donne aussi insolemment.
Tant que ma clé privée ( hinau.lt.pem ) reste cachée au yeux du monde alors je n'ai pas à m'en faire puisque c'est un duo de clé asymétrique.
3 / Utilisation du certificat dans Apache
Actuellement mes virtualhosts repondant aux dns kevin.hinau.lt, www.hinau.lt et *.hinau.lt sont situés dans /etc/apache2/sites-available/hinau.lt/www et n'acceptent que le http :
<VirtualHost *:80> ServerAdmin hinault@gmail.com ServerName kevin.hinau.lt ServerAlias www.hinau.lt ServerAlias *.hinau.lt DocumentRoot /var/hinau.lt/kevin/ <Directory /var/hinau.lt/kevin/> Options Indexes FollowSymLinks MultiViews ExecCGI AllowOverride None Order allow,deny allow from all AddDefaultCharset utf-8 </Directory> ErrorLog /var/log/apache2/hinau.lt/kevin-error.log LogLevel warn CustomLog /var/log/apache2/hinau.lt/kevin-access.log combined ServerSignature Off </VirtualHost>
Si l'on désire la même chose en utilisant les certificats il suffit d'ajouter les quelques lignes suivantes dans un virtualhost répondant au port 443 :
<VirtualHost *:443> ServerAdmin hinault@gmail.com ServerName kevin.hinau.lt ServerAlias www.hinau.lt ServerAlias *.hinau.lt SSLEngine on SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL SSLCertificateFile "/etc/apache2/certs-available/hinau.lt.crt" SSLCertificateKeyFile "/etc/apache2/certs-available/hinau.lt.pem" DocumentRoot /var/hinau.lt/kevin/ <Directory /var/hinau.lt/kevin/> Options Indexes FollowSymLinks MultiViews ExecCGI AllowOverride None Order allow,deny allow from all AddDefaultCharset utf-8 </Directory> ErrorLog /var/log/apache2/hinau.lt/kevin-error.log LogLevel warn CustomLog /var/log/apache2/hinau.lt/kevin-access.log combined ServerSignature Off </VirtualHost>
Sur le papier c'est bien mais il manque un truc : quand on a généré le certificat, openssl a créé une clé publique et une clé privée. Cette dernière est protégée par une passphrase (sorte de mot de passe local sur le fichier) et si celui protège assez efficacement la lecture du fichier il va aussi empêcher le service apache de se lancer correctement puisque celui-ci ne pourra pas le lire :
# /etc/init.d/apache2 restart Forcing reload of web server (apache2)... waiting Apache/2.2.3 mod_ssl/2.2.3 (Pass Phrase Dialog) Some of your private key files are encrypted for security reasons. In order to read them you have to provide the pass phrases. Server hinau.lt:443 (RSA) Enter pass phrase:******** OK: Pass Phrase Dialog successful.
Il faut alors taper le mot de passe au démarrage du service ce qui peut s'avérer impraticable. Aussi on va enlever cette protection mais en laissant seulement root avoir accès à ce fichier ( owner = root et droits ugo = 600) :
# openssl rsa -in hinau.lt.pem -out hinau.lt.key Enter pass phrase for hinau.lt.pem: ******** writing RSA key # chown root:root hinau.lt.key # chmod 600 hinau.lt.key
Il ne reste plus qu'à corriger la ligne :
SSLCertificateKeyFile "/etc/apache2/certs-available/hinau.lt.pem"
en
SSLCertificateKeyFile "/etc/apache2/certs-available/hinau.lt.key"
Et voilà plus de sécurité locale :
# /etc/init.d/apache2 restart Forcing reload of web server (apache2)... waiting .
( Attention je ne préconise pas cette méthode ! Je la signale seulement )
4 / Installation du certificat root de la CA
Si on utilise maintenant un navigateur, Mozilla Firefox par exemple, pour aller sur https://kevin.hinau.lt:1443 (Mon serveur écoute sur le port 1443), celui-ci va me détecter un problème de sécurité : en effet il ne connait pas l'autorité de certification dont dépend mon certificat et donc doute de son authenticité. On aura ce message d'erreur :
Pour corriger ça, on pourrait ajouter une exception cependant le message reviendra à chaque rencontre d'un certificat issue de CACert. Il est préférable d'ajouter la certificat racine dans votre bibliothèque. Il est disponible dans la section Root Certificate. Cliquez sur le certificat de classe 1 dans le format PEM ou DER et il va s'installer tout seul comme un grand après confirmation !
Maintenant vous pourrez accéder à https://kevin.hinau.lt:1443 sans erreur de connexion.
5 / Créer sa propre CA
Vaste sujet ! Cependant comme il est très différent du sujet actuel, je ne donnerais que quelques pistes de projets de PKI à explorer :
- Le complet et complexe : OpenCA
- Le simple, light mais plutôt adapté à OpenVPN : EasyRSA
- Le Rigolo : OpenSSL. Oui il est possible de tout faire à la main et de devenir une CA en ligne de commande !
- La PKI à la française : Rooster. Non testé mais à l'air assez complet.
- L'inconnu au bataillon mais qu'il faudrait essayer un jour de convalescence ou à Noël : openWebPKI
Sur ce !
Article sous CC-by excepté le logo appartenant à CACert.