Mise en place de l'HPKP en utilisant un certificat letsencrypt

apache securite serveur hpkp letsencrypt

Par défaut, letsencrypt génère une clé et une demande de certificat à chaque renouvellement, ce qui modifie l'empreinte du certificat et rend donc HPKP inutilisable. Il existe pourtant un autre moyen de faire: générer un clé et une demande de certificat à la main et laisser à letsencrypt seulement le soin de générer le certificat à partir de ce dernier.

Si letsencrypt est (ou a déjà été utilisé), il est préférable de renommer/déplacer le répertoire /etc/letsencrypt. On commence par construire l'arborescence afin de ne pas mélanger les torchons avec les serviettes. Le fichier de configuration d'un côté, les demandes de certificats d'un autre, les certificats avec les certificats...

mkdir -p /etc/pki/tls/{conf,private,csr}

On va ensuite écrire un petit fichier de configuration openssl pour nos demandes de certificats (csr)

Fichier /etc/pki/tls/conf/example.com.conf

[req]
default_bits       = 4096
prompt             = no
default_md         = sha512
req_extensions     = req_ext
distinguished_name = dn

[dn]
C            = FR
ST           = Occitanie
L            = Beaucaire
O            = Example
OU           = devops
CN           = example.com
emailAddress = webmaster@example.com

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1  = example.com
DNS.2  = www.example.com
DNS.3  = blog.example.com

On génère la clé privée principale et les deux clés privées de secours.

openssl genrsa -out /etc/pki/tls/private/example.com.1.key 4096
openssl genrsa -out /etc/pki/tls/private/example.com.2.key 4096
openssl genrsa -out /etc/pki/tls/private/example.com.3.key 4096

On génère la demande de certificat principale et les demandes de certificat de secours

openssl req -new -key /etc/pki/tls/private/example.com.1.key -sha512 -subj "/CN=example.com/C=FR/ST=Occitanie/L=Beaucaire/O=Example/OU=devops" -out /etc/pki/tls/csr/example.com.1.csr -config <( cat /etc/pki/tls/conf/example.com.conf )
openssl req -new -key /etc/pki/tls/private/example.com.2.key -sha512 -subj "/CN=example.com/C=FR/ST=Occitanie/L=Beaucaire/O=Example/OU=devops" -out /etc/pki/tls/csr/example.com.2.csr -config <( cat /etc/pki/tls/conf/example.com.conf )
openssl req -new -key /etc/pki/tls/private/example.com.3.key -sha512 -subj "/CN=example.com/C=FR/ST=Occitanie/L=Beaucaire/O=Example/OU=devops" -out /etc/pki/tls/csr/example.com.3.csr -config <( cat /etc/pki/tls/conf/example.com.conf )

On vérifie que les demandes de certificat contiennent bien les noms d'hôtes alternatifs

openssl req -noout -text -in /etc/pki/tls/csr/example.com.1.csr
openssl req -noout -text -in /etc/pki/tls/csr/example.com.2.csr
openssl req -noout -text -in /etc/pki/tls/csr/example.com.3.csr

On demande notre certificat, en fournissant une demande de certificat. letsencrypt refusera de générer un certificat ayant le nom fourni si le fichier existe déjà. C'est la raison d'être de l'ID dans le nom de fichier (0) et qui devra être incrémenter à chaque renouvellement réussi.

letsencrypt certonly --csr /etc/pki/tls/csr/example.com.1.csr --agree-tos --webroot --webroot-path /var/www/html --cert-path /etc/pki/tls/certs/example.com.0.crt --chain-path /etc/pki/tls/certs/example.com.0.chain.pem --fullchain-path /etc/pki/tls/certs/example.com.0.fullchain.pem  --cert-name example.com -m webmaster@example.com

On visualise le certificat reçu

openssl x509 -noout -text -in /etc/pki/tls/certs/example.com.0.crt

On peut enfin obtenir nos précieuses empreintes. On remarque que l'on se sert de la demande de certificats et non du certificat, mais on aurait tout aussi bien pu générer l'empreinte à partir de celui-ci. De toute façon ces empreintes ne changeront plus.

openssl req -pubkey < /etc/pki/tls/csr/example.com.1.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
openssl req -pubkey < /etc/pki/tls/csr/example.com.2.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
openssl req -pubkey < /etc/pki/tls/csr/example.com.3.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

Et on ajoute nos empreintes à la configuration apache.

Header always set Public-Key-Pins 'pin-sha256="yx0XVK0m6iAMItPEcQgfcaqTkDQ1kmabKEyM4NRsGfY=";pin-sha256="JENGnxtpMMePxB2//+UZ5nTjvotOCb/Kn2q1xC8hwoI=";pin-sha256="ufCAWYtHpYA2GL5J28oZsFE/zWoWhcJiO+WDX6LseXA=";max-age=2592000;includeSubdomains"

Article précédent Article suivant