Créer une PKI (Public Key Infrastructure) pour générer ses propres certificats SSL/TLS avec Vault
Qu'est-ce qu'une PKI ?
Une Infrastructure à Clés Publiques (PKI, pour Public Key Infrastructure) est un ensemble de rôles, politiques, logiciels, et procédures nécessaires pour créer, gérer, distribuer, utiliser, stocker et révoquer les certificats numériques et gérer les clés publiques.
Ou plus simplement une infrastructure permettant de gérer vos propres certificats SSL/TLS.
Voici quelques avantages à gérer sa propre PKI :
- Contrôle total : vous contrôlez la chaîne complète : de la création à la révocation des certificats.
- Sécurité renforcée : il n'y a plus de dépendance avec un tiers - ce qui réduit les risques de sécurité.
- Coût : le coût peut parfois être moins important que de passer par un tiers.
- Rapidité : une fois que la solution est installée et configurée et que la gestion des certificats est automatisée - la génération ou la révocation d'un certificats sont des opérations simples et rapides.
Dans beaucoup de cas, générer et utiliser des certificats SSL/TLS émis par un tiers de confiance déjà connu est beaucoup plus simple et demande beaucoup moins de travail pour une entreprise.
Attention cependant, l'autorité de certification que vous allez créée ne sera pas reconnue par les navigateurs ou les logiciels.
Pré-requis et environnement
Nous déployons sur l'environnement suivant :
- OS : Ubuntu 22.x
- Version VAULT : 1.15.4
- Domaine : https://vault.tld
- Société : DYNDATA
Le déploiement a été abordé dans un article précèdent. Nous allons opérer via la CLI de vault, cela peut être tout autant être réalisé par l'API.
Structure
Une PKI est structurée par différents niveaux, indiqués dans le schéma.
Nous allons créer la structure suivante :
ROOT CA (Autorité de certification racine) - durée de vie : 10 ans
C'est l'entité de confiance de plus haut niveau - elle créée, émet et gère les certificats racines qui sont à la base de la chaîne de confiance. Ce niveau permet de signer les certificats du niveau inférieur - l'intermediate CA. La clef privée du root ca est ultra sensible et ne doit pas être compromise - nous la générons et stockons hors Vault.
INTERMEDIATE CA - durée de vie : 10 ans
Ce niveau est signé par le root ca. Ce niveau est un relais entre le certificat root et les certificats des niveaux inférieurs.
Si un certificat de ce niveau est compromis - cela n'affecte pas systématiquement toute la PKI, contrairement au root ca.
ISSUING CA - durée de vie : 1 an
Ce niveau inférieur à celui du niveau intermédiaire, est responsable de la génération des certificats finaux, il permet une meilleure organisation de la PKI. Le certificat issuing est signé par l'intermédiaire. En cas de compromission, l'impact sera moins important que pour les niveaux supérieurs.
Génération du ROOT CA hors Vault
En root, on génère le certificat root avec certstrap.
apt install golang-go jq
cd /root/
git clone https://github.com/square/certstrap
cd certstrap
go build
./certstrap --depot-path root init \
--organization "dyndata" \
--common-name "DYNDATA ROOT CA" \
--expires "10 years" \
--curve P-256 \
--path-length 2
Created root/DYNDATA_ROOT_CA.key
Created root/DYNDATA_ROOT_CA.crt
Created root/DYNDATA_ROOT_CA.crl
Il est demandé de taper et retenir une passphrase, nous vous conseillons une passphrase d'une longueur de 20 caractères, avec tout type de caractère.
On peut ensuite vérifier le certificat root, il faut être vigilant sur ces valeurs : O / CN / Validity / Taille de la clef (P-256) :
openssl x509 -in DYNDATA_ROOT_CA.crt -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: ecdsa-with-SHA256
Issuer: O = dyndata, CN = DYNDATA ROOT CA
Validity
Not Before: Dec 18 08:56:43 2023 GMT
Not After : Dec 18 09:05:46 2033 GMT
Subject: O = dyndata, CN = DYNDATA ROOT CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:a5:02:d6:27:2b:d1:23:4a:12:cd:5c:c8:dd:5f:
13:b0:b1:ae:9f:5e:eb:28:eb:83:fe:2a:b8:7c:1f:
f7:3e:bc:e1:03:a3:d7:f2:3a:ba:8f:3c:fc:38:07:
02:6f:b0:e4:ff:99:93:ad:95:85:00:34:31:8f:7b:
56:67:65:96:eb
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:2
X509v3 Subject Key Identifier:
63:11:25:CD:59:A7:4F:0E:53:A5:CA:E0:18:14:6A:07:60:73:D6:3A
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:45:02:20:31:b8:ea:2c:42:2b:82:40:07:19:d5:08:4e:da:
96:e8:c0:0d:8b:5d:b7:6c:52:6c:56:f2:88:95:5b:14:45:e2:
02:21:00:ad:13:d6:1c:66:dc:4a:a9:a1:de:6c:79:9d:ef:c8:
06:88:19:6d:9b:c2:46:4b:26:d6:e7:ef:6e:8d:28:3b:37
Génération de l'INTERMEDIATE CA
Nous allons placer nos fichiers dans /root/pki. Puis on s'identifie avec le bon token.
mkdir /root/pki/ ; cd /root/pki
vault login
On active le chemin (path) "pki_interca" (avec une durée de vie de 10 ans), qui contiendra le certificat intermédiaire :
vault secrets enable -path=pki_interca -description="PKI backend for Intermediate CA" -max-lease-ttl=87600h pki
On indique à Vault les URL issuing + crl :
vault write pki_interca/config/urls issuing_certificates="https://vault.tld:8200/v1/pki_interca/ca" crl_distribution_points="https://vault.tld:8200/v1/pki_interca/crl"
Key Value
--- -----
crl_distribution_points [https://vault.tld:8200/v1/pki_interca/crl]
enable_templating false
issuing_certificates [https://vault.tld:8200/v1/pki_interca/ca]
ocsp_servers []
On génère la clef privée et le CSR pour le certificat intermédiaire :
cd /root/pki
vault write -format=json pki_interca/intermediate/generate/internal key_bits=3072 common_name=DyndataIntermediate ttl=87600h > pki_pki_interca.csr.json
On vérifie le contenu du certificat, il faut être vigilant sur ces valeurs : CN / Public Key Algorithm / Taille de la clef (3072 bits) :
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: CN = DyndataIntermediate
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (3072 bit)
Modulus:
[...]
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:DyndataIntermediate
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
[...]
On signe le certificat intermédiaire avec le certificat root :
cd /root/certstrap/
./certstrap --depot-path root sign \
--CA "DYNDATA ROOT CA" \
--intermediate \
--csr /root/pki/pki_pki_interca.csr \
--expires "10 years" \
--path-length 1 \
--cert /root/pki/pki_pki_interca.crt \
"DYNDATA Intermediate CA"
Building intermediate
Created root/DYNDATA_Intermediate_CA.crt from root/DYNDATA_Intermediate_CA.csr signed by root/DYNDATA_ROOT_CA.key
On ajoute ce certificat intermédiaire signé par le certificat root, dans vault :
cd /root/pki
vault write -format=json pki_interca/intermediate/set-signed certificate=@pki_pki_interca.crt > pki_pki_interca.crt.json ; cat pki_pki_interca.crt.json
{
"request_id": "978ec2a7-ba4e-afbb-3aff-6db9ade8b565",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"existing_issuers": null,
"existing_keys": null,
"imported_issuers": [
"43fe0995-930f-73b7-6051-ae06094543c7"
],
"imported_keys": null,
"mapping": {
"43fe0995-930f-73b7-6051-ae06094543c7": "11183958-c3a4-544d-931d-6c51b95389ef"
}
},
"warnings": null
}
Génération de l'ISSUING CA
On active le chemin (path) "issuing_dyndata_linux" (avec une durée de vie de 10 ans), qui contiendra le certificat issuing :
vault secrets enable -path=issuing_dyndata_linux -max-lease-ttl=87600h pki
Success! Enabled the pki secrets engine at: issuing_dyndata_linux/
On indique à Vault les URL issuing + crl :
vault write issuing_dyndata_linux/config/urls issuing_certificates="https://vault.tld:8200/v1/issuing_dyndata_linux/ca" crl_distribution_points="https://vault.tld:8200/v1/issuing_dyndata_linux/crl"
Key Value
--- -----
crl_distribution_points [https://vault.tld:8200/v1/issuing_dyndata_linux/crl]
enable_templating false
issuing_certificates [https://vault.tld:8200/v1/issuing_dyndata_linux/ca]
ocsp_servers []
On génère la clef privée et le CSR pour le certificat intermédiaire :
cd /root/pki
vault write -format=json issuing_dyndata_linux/intermediate/generate/internal key_bits=3072 ttl=87600h organization="DYNDATA" common_name="DYNDATAissuing-Linux" > issuing_dyndata_linux.csr.json
On vérifie le contenu du certificat, il faut être vigilant sur ces valeurs : CN / Public Key Algorithm / Taille de la clef (3072 bits) :
cat issuing_dyndata_linux.csr.json | jq -r '.data.csr' > issuing_dyndata_linux.csr ; openssl req -text -noout -verify -in issuing_dyndata_linux.csr
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: O = DYNDATA, CN = DYNDATAissuing-Linux
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (3072 bit)
Modulus:
[...]
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:DYNDATAssuing-Linux
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
[...]
On signe le certificat issuing avec le certificat intermédiaire :
cd /root/pki
vault write -format=json pki_interca/root/sign-intermediate organization="DYNDATA" csr=@issuing_dyndata_linux.csr key_bits=3072 ttl=8761h format=pem > issuing_dyndata_linux.csr.json
On vérifie le certificat issuing signé par le certificat intermédiaire :
cat issuing_dyndata_linux.csr.json | jq -r '.data.certificate'> issuing_dyndata_linux.crt ;
openssl x509 -in issuing_dyndata_linux.crt -text -noout
On ajoute ce certificat dans vault :
vault write -format=json issuing_dyndata_linux/intermediate/set-signed certificate=@issuing_dyndata_linux.crt > issuing_dyndata_linux.crt.chain.set-signed.json
Création d'un rôle et génération du certificat
On créé un répertoire spécifique pour placer les SSL générés :
mkdir -p /root/pki/SSL/issuing_dyndata_linux/roles/
cd /root/pki/SSL/issuing_dyndata_linux/roles/
On créé le rôle "frontal" :
vault write issuing_dyndata_linux/roles/frontal \
organization="DYNDATA" \
key_type=rsa \
key_bits=3072 \
ttl=366 \
allow_any_name=true \
allowed_domains=front.tld \
allow_subdomains=true
On génère le SSL final pour le domaine "front.tld" :
issuing_dyndata_linux/issue/frontal \
format=pem_bundle \
common_name="front.tld" \
ttl=8759h \
> /root/pki/SSL/issuing_dyndata_linux/roles/front.tld.pem
Le certificat se trouve à présent ici : /root/certstrap/SSL/issuing_dyndata_linux/roles/front.tld.pem Il y a dans ce .pem : la clef privée et la chaîne complète.
openssl x509 -in /root/pki/SSL/issuing_dyndata_linux/roles/front.tld.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
58:8f:26:65:15:9d:17:2a:a5:1c:8e:e5:b4:c0:93:ef:77:bf:b5:03
Signature Algorithm: sha256WithRSAEncryption
Issuer: O = DYNDATA, CN = DYNDATAissuing-Linux
Validity
Not Before: Dec 19 12:58:52 2023 GMT
Not After : Dec 18 11:59:22 2024 GMT
Subject: CN = front.tld
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
[...]
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment, Key Agreement
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Key Identifier:
03:02:97:89:FF:1F:B9:0D:EF:3D:6A:0C:E7:EE:60:97:5A:2B:33:A2
X509v3 Authority Key Identifier:
31:0D:B0:D2:9E:99:C0:3D:96:6B:53:3C:59:34:69:17:65:99:07:1C
Authority Information Access:
CA Issuers - URI:https://vault.tld:8200/v1/issuing_dyndata_linux/ca
X509v3 Subject Alternative Name:
DNS:front.tld
X509v3 CRL Distribution Points:
Full Name:
URI:https://vault.tld:8200/v1/issuing_dyndata_linux/crl
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
[...]
Généralement, on récupère la clef privée pour l'injecter dans un fichier front.tld.key, et on laisse la chaîne complète dans le .pem.
Certificats root & intermediaires
Comme nous gérons notre propre PKI, celle-ci n'est pas reconnue comme tiers de confiance par les OS et navigateurs. Par conséquent, il est nécessaire d'importer les certificats root et intermédiaires sur les OS et navigateurs faisant appel à des domaines dont le certificat SSL est géré par la PKI.
Aller plus loin...
Nous avons installé une PKI très simplement, il existe de multiples options et de manière d'organiser la PKI. Cependant, nous vous conseillons de lire la documentation officielle.