TLS/SSL

Mise à jour : 8 décembre 2006

Accès à la page d'accueil authentification dans LDAP

Objectif

Ce document a pour objectif de présenter une manière "simple" de mettre en oeuvre le protocole TLS/SSL pour un serveur LDAP.



Retour vers les ressources LDAP.


Préambule

Tout a commencé un jour où j'ai voulu mettre en œuvre une connexion LDAP sécurisé, c'est-à-dire utilisant TLS/SSL.

Bon, ben, j'avoue, c'est pas simple. Surtout quand on débute. En outre, je débute depuis plusieurs mois, et j'ai pas l'impression que j'ai fini de débuter !!!



Si jamais vous vous apercevez que je me suis bien planté, n'hésitez pas à me le dire. Cela me fera gagner du temps.

Egalement, on trouvera ici (signatures et cryptages) quelques essais supplémentaires sur OpenSSL.



Note : Les différentes commandes d'openssll ont été générées avec la version 0.9.7d sous Solaris 8.


Ce que je crois avoir compris

Un client LDAP (C) souhaite établir une connexion TLS/SSL avec un serveur LDAP (S). Dans le premier exemple que j'ai essayé de tester, seul S doit prouver son identité. Le client n'est donc pas contraint de fournir un certificat au serveur. Ce cas est prodigieusement intéressant car de nombreux clients simples sont incapables de fournir un quelconque certificat.

Là où c'est pas évident, c'est qu'il faut :

  1. créer plusieurs certificats,

  2. les gérer (c'est-à-dire les placer correctement aux bons endroits avec de "bons" noms),

  3. les tester (d'abord à l'aide des outils ayant servis à les créer, puis, et c'est le plus important, avec les clients LDAP).


Création des certificats.

J'utilise pour cela OpenSSL. Quelle galère !

Je vais créer 3 certificats, histoire de s'amuser un peu. J'aurais pu en faire 2 seulement, c'est certain, mais cafouiller pour cafouiller, autant aller jusqu'au bout du cauchemar !

Les certificats

Le premier certificat correspondra au certificat d'une autorité racine de certification (root CA). Il en faut une obligatoirement. Son certificat sera donc nécessairement auto-signé. Le nom du fichier contenant le certificat sera ca.pem.

Le deuxième certificat correspondra à une autre autorité de certification (cette autorité sera par exemple l'autorité chargée de valider les certificats de serveurs LDAP) ; toutefois, cette autorité (ldap CA) ne sera pas racine. Son certificat sera donc signé par l'autorité racine root CA. Le nom du fichier contenant le certificat sera ldapca.pem.

Un troisième certificat identifiera le serveur LDAP ; nous appellerons ce certificat mesange.pem. Il sera signé par l'autorité ldap CA.

L'organisation

Ensuite, il va falloir organiser les différents certificats de façon à ce que les clients et serveurs puissent les trouver et les tester.

J'utilise l'organisation proposée dans le fichier de configuration de openssl. Autrement dit, dans le fichier openssl.conf.

La variable d'environnement OPENSSL_CNF permet de spécifier quel fichier de configuration utiliser. Ceci peut être particulièrement utile dans le cas de l'existence de multiples versions de ce type de fichier.

On a donc l'organisation suivante :



Le certificat racine est placé directement dans $dir.
La clé privée du certificat racine est dans $dir/private.


Ensuite va se poser le problème suivant : pour tester la validité d'un certificat, il faut tester la validité de la chaîne complète des certificats jusqu'au certificat racine. Ce qui signifie par exemple qu'un client qui voudra valider le certificat du serveur devra avoir accès aux certificats ldapca.pem et ca.pem.

Il faut donc posséder localement les certificats permettant de tester la chaîne de certificats.

Fichiers de configuration

Bien qu'il soit théoriquement possible de créer et gérer des certificats en utilisant le fichier de configuration principal de openssl (soit, openssl.cnf), il peut être beaucoup plus judicieux d'utiliser divers fichiers de configuration pour les différents cas de figures que l'on va rencontrer. Voici une liste de différents fichiers de configuration. Je ne les ai pas inventés ; je les ai simplement extraits du livre « LDAP system administration, Gerald Carter, O'Reilly® ».



Création des fichiers et répertoires initiaux

Il convient, si ce n'est déjà fait, de créer les répertoires fondamentaux: $dir, $dir/certs, $dir/private et $dir/newcerts.

Puis, lors de la toute première création de certificats, il faut initialiser le fichiers index.txt et serial. Pour cela il suffit de taper les commandes suivantes :

touch index.txt

ce qui créera un fichier index.txt vide, et

echo 01 > serial

ce qui créera un fichier serial avec 1 comme valeur initiale de numéro de certificat.



Création du certificat de l'autorité de certification root CA

Le paramètre x509 de la requête ci-dessous permet d'avoir un certificat auto-signé :

openssl req -x509 -config root-ca-cert.cnf -newkey rsa -out ca.pem -keyout private/ca.key -days 1826

Le certificat est créé pour une durée de 5 ans (soit 1826 jours). N'oubliez pas de spécifier une durée de validité pour ce certificat en particulier. Sinon, on obtiendrait une durée de validité de 0 seconde. Ce qui est assez peu. Ce dernier point m'a permis de tester involontairement le comportement de certains outils lorsqu'ils recevaient un certificat périmé (ou bien dont une autorité de certification était périmée).

La requête demande une phrase de passe (exemple de phrase : the lion sleeps tonight) pour la clé privée, et un DN pour l'identification du certificat.

Le DN choisi est par exemple : cn=INT LOR root CA, ou=LOR, o=INT, l=Evry, st=Essonne, c=FR

Vous pouvez vérifier le contenu de votre certificat en le lisant : lire un certificat.



Création d'un fichier hash.0

Il est important ensuite de créer un fichier (symbolique) dont le nom est de la forme hash.0, dans laquelle hash est la valeur de hachage du DN.

Pour obtenir la valeur de hachage du fichier ca.pem par exemple, il suffit de taper la commande suivante :

openssl x509 -hash -in ca.pem -noout



Le fichier hash.0 est à placer dans le répertoire certs et doit pointer vers le vrai fichier cacert.pem. Exemple :

ln -s ../ca.pem 56ffeb3f.0

Ou bien, en supposant que l'on soit dans le répertoire certs :

ln -s ../ca.pem `openssl x509 -hash -in ../ca.pem -noout`.0



Si ceci n'était pas fait, il serait difficile de vérifier une chaîne de certificats.



Création d'un certificat pour l'autorité ldap CA

Tout d'abord, on crée un certificat temporaire et une clé privée normale :

openssl req -config req-subca-cert.cnf -newkey rsa -out templdapcacert.pem -keyout private/ldapca.key

Il faut également une phrase de passe et un DN.

Le DN est : cn=INT LOR LDAP CA, ou=LOR, o=INT, l=Evry, st=Essonne, c=FR



Signature du certificat de ldap CA par l'autorité racine root CA

A ce moment le certificat signé n'est plus temporaire et il est donc automatiquement placé (grâce à la commande ci-dessous) dans le répertoire newcerts sous le nom serial.pem dans lequel serial est la valeur contenue dans le fichier serial (par exemple, le fichier créé sera 01.pem) :

openssl ca -config ca-subca-cert.cnf -in templdapcacert.pem -notext -out certs/ldapca.pem

La commande ci-dessus crée également un fichier ldapca.pem dans le répertoire certs. Pourquoi ? Eh bien, si le fichier 01.pem est peut-être gentiment créé par openssl, son nom n'est pas très parlant à l'esprit. Il vaut mieux que le nom du fichier soit représentatif du certificat qu'il contient. Cela aide. Plus tard.

L'argument notext permet de ne pas ajouter la description textuelle du certificat dans le fichier final.

Il est important de rajouter les extensions de type ca si l'on veut que le certificat soit celui d'un CA ; voir le fichier de configuration utilisé (ca-subca-cert.cnf)

La requête demande la phrase de passe du certificat de root CA. Puis, la base de données des certificats sera mise à jour (modification des fichiers serial et index.txt)

Le fichier templdapcacert.pem peut alors être détruit.

Même chose pour le fichier symbolique de la forme hash.0 que dans le cas du certificat ca.pem. Par exemple :

ln -s ldapca.pem c9ce0d54.0

ou bien :

ln -s ldapca.pem `openssl x509 -hash -in ldapca.pem -noout`.0



Création d'une requête de certificat pour le serveur

Le serveur LDAP tournera sur la machine nommée « mesange ». La clé privée du futur certificat sera appelée mesange.key. La commande à saisir est de la forme suivante :

openssl req -config req-server-cert.cnf -out tempservercert.pem -keyout private/mesange.key -newkey rsa

Là aussi, une phrase de passe sera demandée.

Le DN est cn=mesange.int-evry.fr, ou=LOR, o=INT, l=Evry, st=Essonne, c=FR

On notera que le cn n'est pas n'importe quoi, mais doit être le nom de la machine sur laquelle tournera le serveur ldap.



Signature du certificat du serveur par l'autorité de certification ldap CA

openssl ca -in tempservercert.pem -cert certs/ldapca.pem -keyfile private/ldapca.key -notext -out certs/mesange.pem

On notera ici qu'il faut préciser quelle CA doit servir à signer le certificat. D'où la présence des deux arguments supplémentaire -cert et -keyfile. La signature exigera de donner la phrase de passe du certificat LDAP CA.

Ne pas oublier de créer un fichier hash.0 selon la méthode habituelle.



Suppression de la phrase de passe du certificat serveur

Il peut être souhaitable de retirer la phrase de passe de la clé privée du certificat serveur. Par exemple, supposons que nous souhaitions supprimer la phrase de passe de la clé privée stockée dans le fichier « mesange.key ». Pour ce faire, nous tapons la commande suivante :

openssl rsa -in private/mesange.key -out private/mesange-free.key

La commande nous demande de saisir la phrase de passe initiale, et génère un nouveau fichier (mesange-free.key) contenant la clé privée, mais cette fois-ci sans phrase de passe.



Lecture d'un certificat

Pour afficher simplement au format texte un certificat contenu dans un fichier certificat.pem, il suffit de taper la commande :

openssl x509 -text -noout -in certificat.pem

dans laquelle :




Phrase de passe (Passphrase) : qu'est-ce que c'est?

Un simple mot de passe n'étant plus forcément suffisant pour assurer la sécurité d'un système (et dans notre cas d'un certificat), on demande à l'utilisateur de donner une phrase entière pour assurer une meilleure authentification.

Exemples de phrases :



Il est tout à fait envisageable d'avoir une phrase de passe limitée à un seul mot. C'est vrai que cela ressemble à un mot de passe. Mais techniquement c'est différent, ne l'oublions pas !

Si vous voulez supprimer une phrase de passe, allez lire à cet endroit.




Test des certificats

openssl verify certs/mesange.pem

ou bien :

openssl verify -Capath certs certs/mesange.pem

permet de vérifier la chaîne de certification du certificat du serveur.

La deuxième version de la commande permet d'aider openssl à trouver où sont situés les fichiers de la forme hash.0.

Mais le mieux consiste à tester réellement les certificats.


Configuration du fichier slapd.conf

Trois clauses sont nécessaires pour un bon fonctionnement du serveur. Voici un exemple de définition. Vous devrez adapter ces définitions à votre cas.

TLSCACertificatePath     /usr/share/ssl/certs
TLSCertificateFile       /usr/share/ssl/certs/mesange.pem
TLSCertificateKeyFile    /usr/share/ssl/private/mesange.key

La première clause permet de spécifier où sont situés les différents certificats, et en particulier les certificats des CA. Nécessaire si le serveur doit valider le certificat du client.

Les deux autres clauses (TLSCertificate et TLSCertificateKeyFile) permettent de spécifier le certificat du serveur et sa clé privée.

Une fois le fichier de configuration du serveur mis à jour, il convient de lancer correctement le serveur. Celui-ci doit pouvoir accepter aussi bien des demandes de connexions non sécurisées que mettant en oeuvre la couche TLS. Ceci signifie que le serveur doit généralement écouter 2 ports différents. Un port supportera les connexions normales, et l'autre les connexions de type TLS. La commande slapd permet ceci. Par exemple :

slapd -d 5 -h "ldap://:9009/ ldaps://:9008" -f slapd.conf

Cette commande ordonne au serveur d'écouter les connexion normales sur le port 9009 et les connexions TLS sur le port 9008. Le paramètre d 5 permet au serveur d'afficher ce qu'il fait, ce qui est très utile lors des tests.

Notons que lors du démarrage du serveur, il vous sera demandé la passe de phrase du certificat serveur.

Il est également possible de tester TLS directement au dessus d'un seul port. Pour cela, il suffit de taper la commande suivante :

slapd -d 5 -h ldap://:9009/ -f slapd.conf

Par contre, il faut que le client puisse utiliser l'extension "StartTLS".

Par exemple, avec le client ldapsearch :

ldapsearch -Z -H ldap://machine:9009/ "(filtre)"

L'extension "StartTLS" est demandée par l'utilisation de l'argument supplémentaire -Z.




Configuration du client ldapsearch

Le client ldapsearch doit pouvoir accéder aux certificats de CA pour être en mesure de valider le certificat du serveur.

Ceci se fait grâce à un fichier (ldaprc ou bien ldap.conf) dans lequel 2 clauses sont importantes :

TLS_CACERT      /usr/local/ssl/ca.pem
TLS_CACERTDIR   /usr/local/ssl/certs

La première clause indique quel est le fichier contenant le root CA. La deuxième clause donne le répertoire où sont stockés les différents certificats nécessaires à la validation.

Note : La première clause n'est pas fondamentalement nécessaire si dans le répertoire des certificats existe un lien symbolique vers le certificat du root CA.

Si vous utilisez un fichier ldaprc, et si dans ce fichier sont définies les deux clauses supplémentaires suivantes :

TLS_CERT      /usr/local/ssl/certs/client.pem
TLS_KEY       /usr/local/ssl/private/client.key

alors, lorsque vous lancerez une commande ldapsearch, la passphrase du certificat client sera demandée, si celle-ci existe bien entendu.




Création d'un certificat client

L'idée de principe est identique à la création d'un certificat serveur. Il faudra simplement choisir le bon fichier de configuration pour générer la requête de certificat, puis il faudra signer le certificat par une autorité de certification.

Je vous laisse faire vous-même vos propres essais. J'y suis arrivé ; et comme vous ne pouvez pas être plus bête que moi (à moins d'être un idiot congénital), vous devriez y arriver vous aussi.


Fichiers de configuration utilisés

Root-ca-cert.cnf

# pour generer un certificat root CA
[ req ]
default_bits        = 1024
default_keyfile     = private/ca.key
default_md          = md5
distinguished_name  = req_distinguished_name
x509_extensions     = rootca_cert


[ req_distinguished_name ]
countryName            = Pays
countryName_default    = FR
countryName_min        = 2
countryName_max        = 2

stateOrProvinceName         = Etat, province ou departement
stateOrProvinceName_default = Essonne

localityName         = Ville
localityName_default = Evry

organizationName         = Organisation
organizationName_default = INT

organizationalUnitName         = Unite organisationnelle
organizationalUnitName_default = LOR

commonName         = Nom commun
commonName_default = INT LOR Root CA
commonName_max     = 64

emailAddress     = Adresse mail
emailAddress_max = 64

[ rootca_cert ]
# la section ci-dessous decrit les extensions a inclure dans un certificat rootCA

basicConstraints       = critical, CA:true
subjectKeyIdentifier   = hash
keyUsage               = critical, keyCertSign, cRLSign
authorityKeyIdentifier = keyid:always,issuer:always
nsCertType             = sslCA, emailCA, objCA
nsComment              = "Certificat Racine. Genere par OpenSSL"
# subjectAltName       = email:copy




req-subca-cert.cnf

# pour generer une requete de certificat CA intermediaire
[ req ]
default_bits         = 1024
default_keyfile      = private/subca.key
default_md           = md5
distinguished_name   = req_distinguished_name
x509_extensions      = subca_req
string_mask          = nombstr

[ req_distinguished_name ]
countryName         = Pays
countryName_default = FR
countryName_min     = 2
countryName_max     = 2

stateOrProvinceName         = Etat, province ou departement
stateOrProvinceName_default = Essonne

localityName         = Ville
localityName_default = Evry

organizationName         = Organisation
organizationName_default = INT

organizationalUnitName         = Unite organisationnelle
organizationalUnitName_default = LOR

commonName         = Nom commun
commonName_default = INT LOR LDAP CA
commonName_max     = 64

emailAddress     = Adresse mail
emailAddress_max = 64


[ subca_req ]
basicConstraints        = critical, CA:true
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid, issuer:always
keyUsage                = critical, keyCertSign, cRLSign
# nsCertType            = sslCA, emailCA, objCA
# nsComment             = "Requete de signature de certificat"
# subjectAltName        = email:copy





req-server-cert.cnf

# pour generer une requete de certificat serveur
[ req ]
default_bits        = 1024
default_keyfile     = private/server.key
default_md          = md5
distinguished_name  = req_distinguished_name
x509_extension      = server_req
string_mask         = nombstr

[ req_distinguished_name ]
countryName         = Pays
countryName_default = FR
countryName_min     = 2
countryName_max     = 2

stateOrProvinceName         = Etat, province ou departement
stateOrProvinceName_default = Essonne

localityName         = Ville
localityName_default = Evry

organizationName         = Organisation
organizationName_default = INT

organizationalUnitName         = Unite organisationnelle
organizationalUnitName_default = LOR

commonName     = Nom commun (ex: nom de la root CA)
commonName_max = 64

emailAddress     = Adresse mail
emailAddress_max = 64

[ server_req ]
basicConstraints      = critical, CA:false
subjectKeyIdentifier  = hash
keyUsage              = digitalSignature, keyEncipherment
extendedKeyUsage      = serverAuth, clientAuth
nsCertType            = server
# nsComment           = "Requete de signature de certificat"
# subjectAltName      = email:copy



req-user-cert.cnf

# poour generer une requete de certificat utilisateur
[ req ]
default_bits        = 1024
default_keyfile     = private/user.key
default_md          = md5
distinguished_name  = req_distinguished_name
x509_extensions     = user_req
string_mask         = nombstr

[ req_distinguished_name ]
countryName         = Pays
countryName_min     = 2
countryName_max     = 2

stateOrProvinceName     = Etat, province ou departement
localityName            = Ville
organizationName        = Organisation
organizationalUnitName  = Unite organisationnelle
commonName              = Nom commun
commonName_max          = 64

emailAddress      = Adresse mail
emailAddress_max  = 64


[ user_req ]
basicConstraints         = critical, CA:false
subjectKeyIdentifier     = hash
keyUsage                 = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage         = clientAuth, emailProtection
nsCertType               = client, email
# nsComment              = "Requete de signature de certificat"

subjectAltName  = email:copy
#issuerAltName  =issuer:copy

#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
nsRevocationUrl = ldap://mesange.int-evry.fr:9009/ou=PKI,o=INT,c=FR?certificateRevocationList?sub?(cn=CRL)
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName




ca-subca-cert.cnf


# pour signer un certificat CA intermediaire
[ ca ]
default_ca     = CA_default           # The default ca section

[ CA_default ]
dir            = /usr/local/ssl       # Where everything is kept
certs          = $dir/certs           # Where the issued certs are kept
crl_dir        = $dir/crl             # Where the issued crl are kept
database       = $dir/index.txt       # database index file.
new_certs_dir  = $dir/newcerts        # default place for new certs.

Certificate    = $dir/ca.pem          # The CA certificate
serial         = $dir/serial          # The current serial number
crl            = $dir/ca.crl          # The current CRL
private_key    = $dir/private/ca.key  # The private key

RANDFILE       = $dir/private/.rand   # private random number file


default_days      = 4383     # how long to certify for
default_crl_days  = 30       # how long before next CRL
default_md        = md5      # which md to use.
Preserve          = no       # keep passed DN ordering


x509_extensions   = subca_cert
copy_extensions   = none
policy            = policy_match


[ subca_cert ]
basicConstraints        = critical, CA:true
authorityKeyIdentifier  = keyid:always, issuer:always
subjectKeyIdentifier    = hash
keyUsage                = critical, keyCertSign, cRLSign
# nsCertType            = sslCA, emailCA, objCA
nsComment               = "Genere par OpenSSL"
# subjectAltName        = email:copy


[ policy_match ]
countryName             = match
stateOrProvinceName     = optional
localityName            = optional
organizationName        = supplied
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional




ca-server.cert.cnf

# pour signer un certificat serveur
[ ca ]
default_ca     = CA_default           # The default ca section

[ CA_default ]
dir            = /usr/local/ssl       # Where everything is kept
certs          = $dir/certs           # Where the issued certs are kept
crl_dir        = $dir/crl             # Where the issued crl are kept
database       = $dir/index.txt       # database index file.
new_certs_dir  = $dir/newcerts        # default place for new certs.

Certificate    = $dir/ca.pem          # The CA certificate
serial         = $dir/serial          # The current serial number
crl            = $dir/ca.crl          # The current CRL
private_key    = $dir/private/ca.key  # The private key

RANDFILE       = $dir/private/.rand   # private random number file

default_days     = 730     # how long to certify for
default_crl_days = 30      # how long before next CRL
default_md       = md5     # which md to use.
Preserve         = no      # keep passed DN ordering

x509_extensions  = server_cert
copy_extensions  = none
policy           = policy_anything

[ server_cert ]
basicConstraints        = critical, CA:false
authorityKeyIdentifier  = keyid:always
subjectKeyIdentifier    = hash
keyUsage                = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage        = serverAuth, clientAuth
nsCertType              = server, objsign
nsComment               = "Certificat serveur genere par OpenSSL pour INT/LOR"

#subjectAltName = email:copy
#issuerAltName = issuer:copy


#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName


[ policy_anything ]
countryName              = supplied
stateOrProvinceName      = optional
localityName             = optional
organizationName         = supplied
organizationalUnitName   = optional
commonName               = supplied
emailAddress             = optional





ca-user-cert.cnf

# pour signer un certificat utilsateur
[ ca ]
default_ca     = CA_default           # The default ca section

[ CA_default ]
dir            = /usr/local/ssl       # Where everything is kept
certs          = $dir/certs           # Where the issued certs are kept
crl_dir        = $dir/crl             # Where the issued crl are kept
database       = $dir/index.txt       # database index file.
new_certs_dir  = $dir/newcerts        # default place for new certs.

Certificate    = $dir/ca.pem          # The CA certificate
serial         = $dir/serial          # The current serial number
crl            = $dir/ca.crl          # The current CRL
private_key    = $dir/private/ca.key  # The private key

RANDFILE       = $dir/private/.rand   # private random number file

default_days     = 365     # how long to certify for
default_crl_days = 30      # how long before next CRL
default_md       = md5     # which md to use.
Preserve         = no      # keep passed DN ordering

x509_extensions  = user_cert
copy_extensions  = none
policy           = policy_anything

[ user_cert ]
basicConstraints        = critical, CA:false
authorityKeyIdentifier  = keyid:always
subjectKeyIdentifier    = hash
keyUsage                = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage        = clientAuth, emailProtection
nsCertType              = client, email, objsign
nsComment               = "Certificat utilisateur genere par OpenSSL pour INT/LOR"

subjectAltName = email:copy
#issuerAltName = issuer:copy

#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
nsRevocationUrl = ldap://mesange.int-evry.fr:9009/ou=PKI,o=INT,c=FR?certificateRevocationList?sub?(cn=CRL)
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName


[ policy_anything ]
countryName            = optional
stateOrProvinceName    = optional
localityName           = optional
organizationName       = optional
organizationalUnitName = optional
commonName             = supplied
emailAddress           = supplied




Cette page a été rédigée directement avec OpenOffice 1.1.3