🖥️ TP — CONSTRUIRE UNE MINI-PKI AVEC OPENSSL⚓︎
Durée : 65 minutes — Individuel
Préparation (3 min)⚓︎
Bash
# Vérifier OpenSSL
openssl version
# Attendu : OpenSSL 3.x.x
# Créer l'arborescence de travail
mkdir -p ~/pki_tp/{rootca,server,client}
mkdir -p ~/pki_tp/rootca/{certs,private,newcerts,crl}
cd ~/pki_tp
chmod 700 rootca/private/
touch rootca/index.txt
echo 1000 > rootca/serial
echo "Arborescence PKI créée :"
find ~/pki_tp -type d | sort
ÉTAPE 1 — Créer la Clé Privée et le Certificat de la Root CA (10 min)⚓︎
1a — Générer la clé privée de la Root CA⚓︎
Bash
# Générer une clé RSA-4096 pour la Root CA (plus longue = plus sûre pour la racine)
openssl genrsa -aes256 -out rootca/private/rootca.key 4096
# -aes256 : Protège la clé privée avec un mot de passe AES-256
# Entrer un mot de passe mémorable : "RootCA_TP_2024!"
# Vérifier la clé générée
ls -lh rootca/private/rootca.key
openssl rsa -in rootca/private/rootca.key -text -noout | head -10
# La clé est chiffrée avec AES-256 (protégée par mot de passe)
file rootca/private/rootca.key
cat rootca/private/rootca.key | head -3
# Devrait afficher : -----BEGIN ENCRYPTED PRIVATE KEY-----
1b — Créer le certificat auto-signé Root CA⚓︎
Bash
# Créer le certificat auto-signé de la Root CA
# Durée : 3650 jours (10 ans) — Normal pour une Root CA
openssl req -new -x509 \
-key rootca/private/rootca.key \
-out rootca/certs/rootca.crt \
-days 3650 \
-sha256 \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=Mon Entreprise Formation/OU=IT Security/CN=Mon Root CA TP/emailAddress=ca@formation.local"
# Explication des options :
# req -new -x509 : Créer un certificat auto-signé (pas une CSR)
# -key : Clé privée à utiliser
# -out : Fichier de sortie
# -days 3650 : Validité 10 ans
# -sha256 : Algorithme de hachage pour la signature
# -subj : Identité de la CA (évite le mode interactif)
# Vérifier le certificat créé
openssl x509 -in rootca/certs/rootca.crt -text -noout
ÉTAPE 2 — Inspecter le Certificat Root CA (5 min)⚓︎
Bash
# Afficher les informations du certificat (version longue)
openssl x509 -in rootca/certs/rootca.crt -text -noout
# Repérer dans la sortie :
# ┌─────────────────────────────────────────────────────────┐
# │ Version: 3 (0x2) │
# │ Serial Number: ... (généré automatiquement) │
# │ Signature Algorithm: sha256WithRSAEncryption │
# │ Issuer: C=FR, O=Mon Entreprise, CN=Mon Root CA TP │
# │ → Issuer = Subject = CERTIFICAT AUTO-SIGNÉ │
# │ Validity: │
# │ Not Before: ... (aujourd'hui) │
# │ Not After: ... (dans 10 ans) │
# │ Subject: C=FR, O=Mon Entreprise, CN=Mon Root CA TP │
# │ Public Key: rsaEncryption (4096 bit) │
# │ X509v3 Basic Constraints: │
# │ CA:TRUE ← Ce certificat EST une CA ! │
# └─────────────────────────────────────────────────────────┘
# Affichage condensé (juste les infos essentielles)
echo "=== SUBJECT (Titulaire) ==="
openssl x509 -in rootca/certs/rootca.crt -subject -noout
echo "=== ISSUER (Signataire) ==="
openssl x509 -in rootca/certs/rootca.crt -issuer -noout
echo "=== VALIDITÉ ==="
openssl x509 -in rootca/certs/rootca.crt -dates -noout
echo "=== EMPREINTE SHA-256 ==="
openssl x509 -in rootca/certs/rootca.crt -fingerprint -sha256 -noout
echo "=== EST-ELLE UNE CA ? ==="
openssl x509 -in rootca/certs/rootca.crt -text -noout | grep -A1 "Basic Constraints"
ÉTAPE 3 — Créer la Clé et la CSR du Serveur Web (10 min)⚓︎
Bash
cd ~/pki_tp
# Générer la clé privée du serveur (RSA-2048 — standard pour serveur)
# Pas de mot de passe pour le serveur web (sinon nginx/Apache redemande le MDP au démarrage)
openssl genrsa -out server/server.key 2048
# Vérifier
echo "=== CLÉ PRIVÉE SERVEUR ==="
openssl rsa -in server/server.key -text -noout | head -5
cat server/server.key | head -3
# -----BEGIN PRIVATE KEY----- (pas de chiffrement)
# Créer la CSR avec un fichier de configuration (pour les SAN)
cat > server/server.cnf << 'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext
[dn]
C = FR
ST = Ile-de-France
L = Paris
O = Mon Entreprise Formation
OU = Serveur Web
CN = monserveur.formation.local
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = monserveur.formation.local
DNS.2 = www.formation.local
DNS.3 = formation.local
IP.1 = 127.0.0.1
IP.2 = 192.168.1.100
EOF
# Générer la CSR à partir du fichier de config
openssl req -new \
-key server/server.key \
-out server/server.csr \
-config server/server.cnf
# Inspecter la CSR (ce qui sera envoyé à l'AC)
echo "=== CONTENU DE LA CSR ==="
openssl req -in server/server.csr -text -noout
# Vérifier que les SAN sont présents dans la CSR
openssl req -in server/server.csr -text -noout | grep -A5 "Subject Alternative"
ÉTAPE 4 — Signer le Certificat Serveur avec la Root CA (10 min)⚓︎
Bash
cd ~/pki_tp
# Fichier de configuration pour l'extension du certificat signé
cat > rootca/v3_ext.cnf << 'EOF'
[v3_server]
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = monserveur.formation.local
DNS.2 = www.formation.local
DNS.3 = formation.local
IP.1 = 127.0.0.1
IP.2 = 192.168.1.100
EOF
# Signer la CSR avec la Root CA → Certificat serveur
openssl x509 -req \
-in server/server.csr \
-CA rootca/certs/rootca.crt \
-CAkey rootca/private/rootca.key \
-CAcreateserial \
-out server/server.crt \
-days 365 \
-sha256 \
-extfile rootca/v3_ext.cnf \
-extensions v3_server
# Entrer le mot de passe de la Root CA : "RootCA_TP_2024!"
# Explication :
# -req : On signe une CSR
# -in server.csr : La CSR à signer
# -CA / -CAkey : Le certificat et la clé privée de l'AC signataire
# -CAcreateserial: Génère un numéro de série unique
# -extfile : Fichier d'extensions (SAN, keyUsage, etc.)
# -extensions : Quelle section du fichier d'extension utiliser
echo "=== CERTIFICAT SERVEUR SIGNÉ ==="
openssl x509 -in server/server.crt -text -noout
ÉTAPE 5 — Inspecter et Vérifier la Chaîne (10 min)⚓︎
Bash
cd ~/pki_tp
echo "========================================"
echo "=== INSPECTION DU CERTIFICAT SERVEUR ==="
echo "========================================"
echo ""
echo "--- Subject (Titulaire du cert) ---"
openssl x509 -in server/server.crt -subject -noout
echo ""
echo "--- Issuer (Qui l'a signé) ---"
openssl x509 -in server/server.crt -issuer -noout
echo ""
echo "--- Validité ---"
openssl x509 -in server/server.crt -dates -noout
echo ""
echo "--- Subject Alternative Names ---"
openssl x509 -in server/server.crt -text -noout | grep -A10 "Subject Alternative Name"
echo ""
echo "--- Basic Constraints (est-ce une CA ?) ---"
openssl x509 -in server/server.crt -text -noout | grep -A2 "Basic Constraints"
# CA:FALSE → C'est bien un certificat final, pas une CA
echo ""
echo "--- Key Usage ---"
openssl x509 -in server/server.crt -text -noout | grep -A3 "Key Usage"
echo ""
echo "========================================="
echo "=== VÉRIFICATION DE LA CHAÎNE DE CONFIANCE ==="
echo "========================================="
# Vérifier que le certificat serveur est valide selon notre Root CA
openssl verify -CAfile rootca/certs/rootca.crt server/server.crt
# Attendu : server/server.crt: OK ✅
# Vérifier la relation entre la clé et le certificat
echo ""
echo "--- Hash de la clé privée ---"
openssl rsa -noout -modulus -in server/server.key | openssl md5
echo "--- Hash de la clé publique dans le certificat ---"
openssl x509 -noout -modulus -in server/server.crt | openssl md5
# Les 2 hash doivent être IDENTIQUES → Clé et certificat correspondent ✅
ÉTAPE 6 — Créer un Certificat Auto-Signé Simple (Comparaison) (7 min)⚓︎
Bash
cd ~/pki_tp
# Créer un certificat auto-signé en une seule commande (sans CA)
openssl req -new -x509 \
-newkey rsa:2048 \
-keyout server/self_signed.key \
-out server/self_signed.crt \
-days 365 \
-sha256 \
-nodes \
-subj "/C=FR/O=Test/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
# -nodes : Pas de mot de passe sur la clé privée (pour tests)
# -addext: Ajouter des extensions directement (OpenSSL 3.x)
echo ""
echo "=== COMPARAISON AUTO-SIGNÉ vs SIGNÉ PAR CA ==="
echo ""
echo "--- Certificat AUTO-SIGNÉ ---"
openssl x509 -in server/self_signed.crt -subject -noout
openssl x509 -in server/self_signed.crt -issuer -noout
echo "Subject = Issuer ? (auto-signé = OUI)"
echo ""
echo "--- Certificat SIGNÉ PAR CA ---"
openssl x509 -in server/server.crt -subject -noout
openssl x509 -in server/server.crt -issuer -noout
echo "Subject ≠ Issuer ? (signé par CA = OUI)"
echo ""
echo "=== TENTATIVE DE VÉRIFICATION ==="
echo "Vérifier auto-signé avec notre Root CA :"
openssl verify -CAfile rootca/certs/rootca.crt server/self_signed.crt
# → ERREUR : self signed certificate (attendu ✅)
echo ""
echo "Vérifier certificat serveur avec notre Root CA :"
openssl verify -CAfile rootca/certs/rootca.crt server/server.crt
# → OK ✅
ÉTAPE 7 — Formats et Conversions (5 min)⚓︎
Bash
cd ~/pki_tp
# Format PEM (ce qu'on a créé) — Texte base64
echo "=== FORMAT PEM (ASCII/Base64) ==="
cat server/server.crt | head -5
cat server/server.crt | tail -3
echo "Taille : $(wc -c < server/server.crt) octets"
# Convertir PEM → DER (format binaire)
openssl x509 -in server/server.crt -outform DER -out server/server.der
echo ""
echo "=== FORMAT DER (Binaire) ==="
xxd server/server.der | head -3
echo "Taille : $(wc -c < server/server.der) octets"
# Convertir en PKCS#12 / PFX (format Windows, contient clé + cert + chaîne)
cat server/server.crt rootca/certs/rootca.crt > server/fullchain.crt
openssl pkcs12 -export \
-inkey server/server.key \
-in server/server.crt \
-certfile rootca/certs/rootca.crt \
-out server/server.p12 \
-name "Certificat Serveur Formation" \
-passout pass:Export2024!
echo ""
echo "=== FORMAT PKCS#12 / PFX (Windows) ==="
ls -lh server/server.p12
openssl pkcs12 -info -in server/server.p12 -passin pass:Export2024! -nokeys 2>/dev/null | head -15
echo ""
echo "=== RÉCAPITULATIF DES FICHIERS ==="
ls -lh ~/pki_tp/server/ ~/pki_tp/rootca/certs/ ~/pki_tp/rootca/private/
ÉTAPE 8 (BONUS) — Serveur HTTPS avec nginx (10 min)⚓︎
Uniquement si nginx est installé
Bash
# Vérifier nginx
nginx -v 2>/dev/null || echo "nginx non installé — sudo apt install nginx"
# Copier les fichiers dans les répertoires nginx
sudo cp server/server.crt /etc/nginx/ssl/
sudo cp server/server.key /etc/nginx/ssl/
sudo cp rootca/certs/rootca.crt /etc/nginx/ssl/
# Configuration nginx HTTPS minimal
sudo tee /etc/nginx/sites-available/tp-pki << 'NGINX_CONF'
server {
listen 443 ssl;
server_name monserveur.formation.local localhost;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# Chaîne de confiance (certificats intermédiaires si nécessaire)
# ssl_trusted_certificate /etc/nginx/ssl/rootca.crt;
# TLS 1.3 uniquement
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# HSTS (forcer HTTPS)
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
return 200 '<html><body>
<h1>✅ HTTPS fonctionne !</h1>
<p>Certificat signé par Mon Root CA TP</p>
<p>TLS 1.3 activé</p>
</body></html>';
add_header Content-Type text/html;
}
}
server {
listen 80;
server_name monserveur.formation.local localhost;
return 301 https://$server_name$request_uri;
}
NGINX_CONF
sudo ln -sf /etc/nginx/sites-available/tp-pki /etc/nginx/sites-enabled/
sudo nginx -t && sudo nginx -s reload
# Tester avec curl en faisant confiance à notre Root CA
curl --cacert rootca/certs/rootca.crt https://localhost/
# → Devrait afficher le HTML sans erreur ✅
# Tester sans faire confiance à notre Root CA
curl https://localhost/ 2>&1 | head -3
# → curl: (60) SSL certificate problem: self-signed certificate in chain ❌
# C'est normal ! Notre Root CA n'est pas dans le trust store système.
Questions de Réflexion (À rendre)⚓︎
📋 Texte
QUESTIONS DE RÉFLEXION — PKI TP
═══════════════════════════════════════════════════════════════
Q1. Expliquez la différence entre ces 3 fichiers que vous avez créés :
server.key / server.csr / server.crt
Lequel ne doit JAMAIS être partagé ? Pourquoi ?
──────────────────────────────────────────────────────────────
server.key : ___________________________________________
server.csr : ___________________________________________
server.crt : ___________________________________________
Ne jamais partager : ___ car _________________________
Q2. Vous avez créé un certificat SIGNÉ PAR VOTRE Root CA.
Pourquoi le navigateur Chrome affiche-t-il quand même
une alerte de sécurité si vous visitez le site ?
Comment résoudre ce problème en entreprise ?
──────────────────────────────────────────────────────────────
Raison de l'alerte : ___________________________________
Solution en entreprise : _______________________________
Q3. Quelle est la différence entre la Root CA que vous avez créée
et une Root CA comme DigiCert ou Let's Encrypt ?
──────────────────────────────────────────────────────────────
Différence technique : _________________________________
Différence pratique : _________________________________
Q4. Le directeur IT vous demande de créer un certificat pour
couvrir ces 5 sous-domaines : www.acme.fr, api.acme.fr,
mail.acme.fr, vpn.acme.fr, intranet.acme.fr
Deux options : Wildcard *.acme.fr ou certificat multi-domaines SAN.
Quelle option choisissez-vous et pourquoi ?
──────────────────────────────────────────────────────────────
Mon choix : ____________________________________________
Justification : ________________________________________
Q5. Rédigez la commande OpenSSL pour inspecter le certificat
de n'importe quel site web directement depuis le terminal :
──────────────────────────────────────────────────────────────
(Indice : openssl s_client + openssl x509)
Commande : _____________________________________________
═══════════════════════════════════════════════════════════════
CORRIGÉ
═══════════════════════════════════════════════════════════════
Q1 : server.key = Clé PRIVÉE (jamais partagée)
server.csr = Demande de certificat envoyée à l'AC
server.crt = Certificat signé par l'AC (partageable)
Ne jamais partager : server.key — Si volée, n'importe qui peut
se faire passer pour le serveur (usurpation d'identité)
Q2 : Notre Root CA n'est pas dans le trust store du navigateur
(Chrome, Firefox ont leurs propres listes d'AC de confiance).
Solution en entreprise : Déployer le certificat Root CA
dans le trust store via GPO (Group Policy Object) Active Directory,
ou via le gestionnaire de certificats de l'OS.
Q3 : Technique : Même technologie (X.509, RSA, SHA-256)
Pratique : DigiCert/Let's Encrypt sont dans les trust stores
de tous les navigateurs/OS. Notre CA ne l'est pas.
→ Notre CA = Connue seulement des systèmes où on l'a importée.
Q4 : Les deux fonctionnent mais contextes différents :
Wildcard *.acme.fr : 1 certificat pour TOUS les sous-domaines
actuels et futurs → Plus simple à gérer, mais compromis = tous
les sous-domaines affectés.
SAN : Contrôle précis des domaines couverts → Plus sécurisé.
Pour 5 sous-domaines connus : SAN recommandé.
Si sous-domaines fréquents et dynamiques : Wildcard plus pratique.
Q5 : echo "" | openssl s_client -connect exemple.fr:443 -servername exemple.fr 2>/dev/null | openssl x509 -text -noout
═══════════════════════════════════════════════════════════════