Authentification forte par certificats et transfert de certificats de Apache vers Tomcat.

Pour certaines applications sensibles, on préfère utiliser un système d’authentification forte par certificats, plutôt que des couples login/mot de passe. C’est de plus en plus le cas avec certaines applications Web ou encore des WebServices devenant des points d’entrées critiques dans le SI des clients.

Le principe de l’authentification forte est un principe de vérification mutuelle, le client à la connexion va vérifier le certificat présenté par le serveur (notamment l’autorité de certification qui signe les certificats) et inversement le serveur va vérifier que le certificat du client est valide et autorisé.

Schéma de l'authentification mutuelle

Dans ce système on utilise des magasins pour stocker les clefs:

  • Les keystores stockent les clefs privées destinées à chiffrer les informations avant émission.
  • Les truststores stockent les clefs publiques destinées à identifier les interlocuteurs puis à déchiffrer leurs messages.

Générons un certificat pour notre autorité de certification chargée de signer les futurs certificats serveur et client.

# Création d'une clef privée pour notre CA, ainsi qu'un certificate request
openssl req -new -newkey rsa:1024 -nodes -out ca.csr -keyout ca.key -config ".\openssl.cnf" -subj /CN=aldheris.fr/OU=NET/O=ALDHERIS/L=LYON/ST=RHONE ALPES/C=FR/emailAddress=slebreton@aldheris.fr

# Création d'un certificat "self-signed" pour l'autorité
openssl x509 -trustout -signkey ca.key -days 3650 -req -in ca.csr -out ca.pem

# Création d'un numéro de série pour le certificat de l'autorité
echo 02 > ca.srl

# Conversion des certificats PEM vers le format DER
openssl x509 -outform der -in ca.pem -out ca.crt

Ensuite générons un certificat serveur :

# Création d'un keystore pour le serveur avec la clef privée
keytool -genkey -alias server -keyalg rsa -keysize 1024 -keystore server-keystore.jks -storetype JKS -storepass password -keypass password -dname "CN=aldheris.fr, OU=NET, O=ALDHERIS, L=LYON, ST=RHONE ALPES, C=FR, emailAddress= slebreton@aldheris.fr"

# Creation d'un certificate request pour le serveur 
keytool -certreq -keyalg RSA -alias server -file server.csr -keystore server-keystore.jks -storepass password -keypass password

# Signature du certificat serveur avec la CA
openssl x509 -CA ca.pem -CAkey ca.key -CAserial ca.srl -req -in server.csr -out server.pem -days 3650

# Import de la CA dans le keystore serveur
keytool -import -alias aldheris_ca -keystore server-keystore.jks -trustcacerts -file ca.pem -storepass password -keypass password -noprompt

# Import du certificat serveur dans le keystore serveur
keytool -import -alias server -keystore server-keystore.jks -trustcacerts -file server.pem -storepass password -keypass password

# Import de la CA dans le truststore serveur
keytool -import -alias aldheris_ca -keystore server-truststore.jks -trustcacerts -file ca.pem -noprompt -storepass password -keypass password

# Extraction de la clef privée du serveur
java ExportPriv server-keystore.jks server password > server.tmp

# Conversion de la clef serveur PKCS#8 PEM vers le format RSA
openssl pkcs8 -inform PEM -nocrypt -in server.tmp -out server.key

# Conversion des certificats PEM vers le format DER
openssl x509 -outform der -in server.pem -out server.crt

Enfin un certificat client :

# Création d'un certificate request pour le client
openssl req -new -newkey rsa:1024 -nodes -out client.csr -keyout client.key -config ".\openssl.cnf" -subj /CN=Client/OU=NET/O=ALDHERIS/L=LYON/ST=RHONE ALPES/C=FR/emailAddress=slebreton@aldheris.com

# Signature du certificat client avec la CA
openssl x509 –CA ca.pem -CAkey ca.key -CAserial ca.srl -req -in client.csr -out client.pem -days 3650

# Import du certificat client dans le truststore serveur
keytool -import -alias client -keystore server-truststore.jks -trustcacerts -file client.pem -storepass password -keypass password -noprompt

# Import de la CA dans le truststore client
keytool -import -alias aldheris_ca -keystore client-truststore.jks -trustcacerts -file ca.pem -storepass pasword -keypass password -noprompt

# Import du certificat serveur dans le truststore client
keytool -import -alias server -keystore client-truststore.jks -trustcacerts -file server.pem -storepass password -keypass password -noprompt

# Export du certificat client (clef prive+publique) au format PKCS12
openssl pkcs12 -export -clcerts -in client.pem -inkey client.key -out client.p12 -name client -password password

# Conversion des certificats PEM vers le format DER
openssl x509 -outform der -in client.pem -out client.crt

Exemple de configuration avec Apache en utilisant le module SSL :

SSLVerifyClient require
SSLCertificateFile "server.pem"
SSLCertificateKeyFile "server.key"
SSLCertificateChainFile "ca.pem"
SSLCACertificateFile "ca.pem"

Note : Dans cet exemple simple tous les certificats clients signés par notre autorité seront autorisés.

Autre exemple de configuration avec Tomcat en utilisant véritablement les magasins :

 <Connector SSLEnabled="true" 
            clientAuth="true"
            keystoreFile="server-keystore.jks"
            keystorePass="password" 
            secure="true"
            sslProtocol="TLS"            
            truststoreFile="server-truststore.jks" 
            truststorePass="password" />

Nous pouvons faire un premier test avec un navigateur :

Echec de l'authentification

L’accès est refusé, en effet le navigateur n’est pas encore capable de présenter les certificats nécessaires à notre identification. Pour cela il suffit d’aller dans le magasin de certificats de notre navigateur puis :

  1. d’ajouter l’autorité de certification (ca.crt)
  2. d’ajouter le certificat client (client.p12) aux certificats personnels

Succès de l'authentification

Maintenant l’authentification fonctionne.

En java, côté client, le système est assez transparent et prends la main dès que des connexion HTTPS sont effectuées (de façon similaire à la gestion des proxy HTTP). Il suffit d’indiquer au système où trouver les magasins nécessaires :

System.setProperty("javax.net.ssl.keyStoreType","pkcs12");
System.setProperty("javax.net.ssl.keyStore","client.p12");
System.setProperty("javax.net.ssl.keyStorePassword","password");
System.setProperty("javax.net.ssl.trustStoreType","jks");
System.setProperty("javax.net.ssl.trustStore","client-truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword","password");

Transfert de certificats de Apache vers Tomcat

Lorsque l’on travaille sur différents serveurs d’applications Java, il est souvent pratique d’utiliser en amont un serveur Apache en reverse proxy afin de bénéficier de toutes les fonctionnalités courantes offertes par la plateforme (réécriture d’URL, balancing, authentification forte par certificats, etc.).

Or lorsque l’on choisis d’utiliser un système d’authentification forte par certificats, il peut être particulièrement utile de laisser Apache gérer cette authentification tout en restant capable de récupérer ce certificat côté « serveur d’application » pour effectuer des traitements additionnels (vérifications ou comportements de l’application paramétrés en fonction des propriétés du certificat).

Voici comment configurer Apache pour transférer les certificats vers un serveur Tomcat :

Attention il faut absolument utiliser le protocole AJP pour lier Apache et Tomcat.

SSLOptions +ExportCertData +StdEnvVars

Ceci fonctionne même dans un contexte de balancing :

<Proxy balancer://wsCluster>
    BalancerMember ajp://main:9009/ws/services/
    BalancerMember ajp://spare:9009/ws/services/ 
</Proxy>

ProxyRequests Off
ProxyPreserveHost on

<Location /ws/>
    ProxyPass balancer://wsCluster/
    ProxyPassReverse balancer://wsCluster/
</Location>

En java, côté serveur, il est alors possible de récupérer le certificat de la façon suivante :

if (!request.isSecure())
	throw new Exception("La connexion DOIT être sécurisée");

X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
sslsession = (String) request.getAttribute("javax.servlet.request.ssl_session");

Les commentaires ont été fermés.