samedi 10 mars 2012

Authentification SSL client avec Apache

Dans ce billet, nous allons voir comment sécuriser une application WEB afin qu'elle soit accessible uniquement à des terminaux autorisés. Pour cela, nous allons utiliser des certificats clients et faire en sorte que le serveur impose au client de lui présenter un certificat signé par une autorité de certification particulière.

Création des certificats

Création d'une autorité de certification

La première étape consiste à créer notre propre autorité de certification. Nous générons pour cela une clé RSA que nous stockons sous ca.key. A partir de cette clé, nous allons générer un certificat auto-signé. Grace aux commandes openssl ci dessous, nous obtenons les éléments suivants :
ca.key
La clé RSA de notre autorité de certification
ca.crt
Le certificat auto-signé de notre autorité
ca.public.key
La clé publique de notre autorité de certification
openssl genrsa -out ca.key
openssl req -new -key ./ca.key -out ./ca.csr
openssl x509 -req -days 365 -in ./ca.csr -out ./ca.crt -signkey ./ca.key
openssl x509 -in ca.crt -text
openssl rsa -in ca.key -passin pass:XXXX -pubout -out ca.public.key

Création et signature du certificat serveur

Il faut ensuite créer la clé et le certificat qui seront utilisé par le serveur. Le certificat serveur sera signé par notre propre autorité de certification créée à l'étape précédente. Lors de la création de la certificate signing request, il est demandé de renseigner un Distinguished Name (DN). La valeur de ce champ devra être identique à la valeur du ServerName utilisé dans le fichier de configuration Apache httpd.conf. En plus de la clé et du certificat, il peut être intéressant de générer une clé qui ne soit pas protégée par un mot de passe. Cela permet de ne pas avoir à saisir de mot de passe à chaque démarrage du serveur Apache.
openssl genrsa -des3 -out server.key
openssl req -new -key ./server.key -out server.csr
openssl x509 -req -in ./server.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial -out ./server.crt -days 365
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name "server certificate"
openssl pkcs12 -info -in server.p12
openssl rsa -in server.key -passin pass:XXXX -pubout -out server.public.key
openssl rsa -in server.key -out server.nopassphrase.key

Création et signature du certificat client

Nous allons maintenant créer de manière similaire un certificat client qui sera utiliser par le client pour s'authentifier auprès du serveur distant. Le certificat client sera signé par la même autorité de certification que le certificat du serveur.
openssl genrsa -des3 -out client.key
openssl req -new -key ./client.key -out client.csr
openssl x509 -req -in ./client.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial -out ./client.crt -days 365
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "client certificate"
openssl pkcs12 -info -in client.p12
openssl rsa -in client.key -passin pass:XXXX -pubout -out client.public.key

Configuration du serveur Apache

Il convient maintenant de configurer le serveur Apache que seuls les clients disposant d'un certificat signé par notre propre autorité de certification soient autorisés à se connecter. Dans le VirtualHost du port SSL par défaut, il suffit d'utiliser les directives suivantes :
# Doit correspondre au DN du certificat server
ServerName domain

# Encryption et certificat serveur
SSLEngine On
SSLCertificateFile conf/certs/server.crt
SSLCertificateKeyFile conf/certs/server.nopassphrase.key

# Impose la présentation d'un certificat client
SSLVerifyClient require
SSLVerifyDepth 2

# Certificat de l'autorité de certification reconnue pour les clients
SSLCACertificateFile conf/certs/ca.crt
SSLVerifyClient
La valeur require permet d'imposer au client la présentation d'un certificat X509 lors de la phase de négociation SSL.
SSLVerifyDepth
Indique le nombre maximum de niveau que le serveur va analyser pour décider s'il juge le certificat client acceptable. Une valeur de 0 permet de n'autoriser que les certificats clients auto-signés. Une valeur de 1 permet d'accepter les certificats clients auto-signés ou certifiés par une autorité de certification directement connue par le serveur. Dans notre cas, nous utilisons la valeur 2 afin de n'accepter que les certificats clients signés par une autorité de certification directement connue par le serveur.
SSLCACertificateFile
Cette directive permet de spécifier le certificat de l'autorité de certification reconnue par le serveur. Seuls les certificats clients signés par l'autorité de certification identifiée par ce certificat seront acceptés.
Dans un prochain article, nous verrons comment configurer un client Android pour accéder à ce serveur sécurisé.

5 commentaires:

  1. bonjours


    je trouve cette article très intéressent, très bien ,

    merci

    RépondreSupprimer
  2. bonjour
    j 2 question s'il y a une personne qui peut me répondre
    1/ j un problème lors du démarrage du serveur apache "invalide commande SSLEngine,...
    et quand je met cet commande en commentaire le suivante c la mm erreur etc

    2/ le DN quand doit spécifier dans l’étape de "Création et signature du certificat serveur" si je voie le nameserver c 127.0.0.1:8887 et il y a encore nameserver 127.0.0.1:443 je c pas le quelle utiliser ??


    merci d'avance.

    RépondreSupprimer
  3. Pour le SSEngine, tu peux regarder la doc Apache à ce sujet : http://httpd.apache.org/docs/2.0/mod/mod_ssl.html

    En ce qui concerne l'IP à utiliser dans le certificat, l'idéal est d'utiliser le DNS ou l'IP à laquelle le serveur sera accessible des clients. Ce DNS ou cette IP étant également à positionner au niveau du Listen dans le fichier httpd.conf.

    RépondreSupprimer
  4. Bonjour, Merci pour ce tutoriel.

    J'ai un soucis lors de l'accès au serveur en https.
    Tout semble bien se dérouler, le navigateur me propose d'utiliser le certificat client pour la connexion et ensuite j'ai cette erreur : Erreur 117 (net::ERR_BAD_SSL_CLIENT_AUTH_CERT) : Certificat d'authentification de client SSL incorrect.

    Avez-vous une idée ?

    Merci de votre aide.

    RépondreSupprimer
  5. La valeur de SSLVerifyDepth dans votre conf doit être à l'origine du problème. Si vous utiliser la valeur 2, il faut référencer le certificat utilisé pour signer le certificat client (on appelle ce certificat CAcert). Il doit être référencé via la propriété SSLCACertificateFile.

    Faites le test avec SSLVerifyDepth = 0 ou 1.

    RépondreSupprimer

Fork me on GitHub