Sommaire
- 1. Versions, configuration et conventions
- 2. Génération et gestion des clés
- 3. CSR avec SAN
- 4. AC de test locale complète
- 5. Inspection de certificats, CSR, CRL
- 6. Diagnostiquer un serveur avec s_client
- 7. Vérifier une chaîne, une CRL, l'OCSP
- 8. Conversions de formats (PEM, DER, PKCS#7, PKCS#12, JKS)
- 9. Appariement clé / certificat / CSR
- 10. Chiffrement, signature, hash et HMAC
- 11. Tester protocoles et suites
- 12. STARTTLS et protocoles applicatifs
- 13. Performance : speed et s_time
- 14. OpenSSL 3 : providers, legacy, FIPS
- 15. Équivalents Windows : certutil et PowerShell
- 16. Pièges et tableau récapitulatif
Conventions : les exemples visent OpenSSL 3.x (Linux, macOS, ou
openssl.exe fourni avec Git for Windows). Noms et adresses fictifs (
example.com,
192.0.2.0/24). Pour l'architecture d'une PKI, la révocation et ACME, voir
PKI et gestion des certificats.
1. Versions, configuration et conventions
1.1 Branches en 2026
| Branche | Statut | Remarques |
| 1.0.2 / 1.1.1 | Fin de vie publique (1.1.1 : septembre 2023) | Encore présentes sur de vieilles distributions et appliances ; support étendu payant uniquement |
| 3.0 (LTS) | Fin de support en septembre 2026 | Base de RHEL 9, Debian 12, Ubuntu 22.04 : les distributions maintiennent leurs propres correctifs |
| 3.5 (LTS) | Support jusqu'en 2030 | Algorithmes post-quantiques ML-KEM, ML-DSA, SLH-DSA ; groupe hybride X25519MLKEM768 par défaut |
| 3.x non LTS | Environ 13 mois de support chacune | Nouvelles fonctions ; vérifier le calendrier sur openssl-library.org |
openssl version -a # version, options de compilation, OPENSSLDIR, MODULESDIR
openssl version -d # répertoire de configuration (openssl.cnf, certs/)
openssl list -providers # providers chargés (OpenSSL 3)
openssl help # liste des sous-commandes ; "openssl <cmd> -help" pour les options
1.2 Passer les secrets proprement
Les options -passin / -passout acceptent plusieurs sources. pass: expose le secret dans ps et l'historique du shell : à éviter hors laboratoire.
| Syntaxe | Source | Usage |
pass:secret | Ligne de commande | Tests uniquement |
env:VAR | Variable d'environnement | CI/CD (variable masquée) |
file:chemin | Première ligne d'un fichier | Scripts (fichier 600) |
fd:N | Descripteur de fichier | Passage par pipe |
stdin | Entrée standard | Saisie ou pipe |
1.3 Formats et extensions de fichiers
| Format | Encodage | Contenu | Extensions usuelles |
| PEM | Base64 + en-têtes -----BEGIN ...----- | Un ou plusieurs objets (cert, clé, CSR, CRL) | .pem, .crt, .cer, .key, .csr |
| DER | Binaire ASN.1 | Un seul objet | .der, .cer, .crl |
| PKCS#7 | PEM ou DER | Certificats et CRL, pas de clé | .p7b, .p7c |
| PKCS#8 | PEM ou DER | Clé privée (tout algorithme), chiffrée ou non | .key, .p8 |
| PKCS#1 | PEM (BEGIN RSA PRIVATE KEY) | Clé RSA « traditionnelle » | .key |
| PKCS#12 | Binaire | Clé + certificat + chaîne, protégé par mot de passe | .pfx, .p12 |
| JKS | Binaire (Java) | Keystore Java propriétaire | .jks, .keystore |
L'extension ne dit rien : un .cer peut être PEM ou DER. Pour savoir : head -c 30 fichier (texte -----BEGIN = PEM) ou openssl asn1parse -in fichier -inform DER.
2. Génération et gestion des clés
genpkey est la commande unifiée (tout algorithme, sortie PKCS#8). genrsa et ecparam -genkey restent disponibles mais sont historiques.
# RSA 3072 bits (2048 minimum, 3072 recommandé pour une durée longue)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out rsa.key
# ECDSA P-256 (prime256v1 / secp256r1) - choix par défaut pour un serveur TLS
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve -out ec256.key
# ECDSA P-384
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -pkeyopt ec_param_enc:named_curve -out ec384.key
# Ed25519 (SSH, signatures applicatives ; peu supporté par les AC TLS publiques)
openssl genpkey -algorithm ED25519 -out ed25519.key
# X25519 (échange de clés uniquement, pas de signature)
openssl genpkey -algorithm X25519 -out x25519.key
# Post-quantique (OpenSSL 3.5+) : signature ML-DSA, pour laboratoire
openssl genpkey -algorithm ML-DSA-65 -out mldsa65.key
# Restreindre les droits AVANT d'écrire la clé
umask 077; openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out srv.key
2.1 Chiffrer, déchiffrer, inspecter une clé
# Générer directement une clé chiffrée (AES-256, mot de passe depuis un fichier)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -aes-256-cbc -pass file:./cle.pass -out ec.key.enc
# Chiffrer une clé existante / retirer la protection (pour un service sans saisie)
openssl pkey -in srv.key -aes-256-cbc -out srv.key.enc
openssl pkey -in srv.key.enc -out srv.key
# Afficher les paramètres (taille, courbe) sans divulguer la clé dans les logs
openssl pkey -in srv.key -noout -text_pub
openssl pkey -in srv.key -noout -text | head -3
# Vérifier la cohérence interne d'une clé
openssl pkey -in srv.key -check -noout # OpenSSL 3
openssl rsa -in rsa.key -check -noout # RSA
# Extraire la clé publique
openssl pkey -in srv.key -pubout -out srv.pub
# PKCS#8 (BEGIN PRIVATE KEY) vers PKCS#1 (BEGIN RSA PRIVATE KEY) pour un vieux logiciel
openssl rsa -in rsa.key -traditional -out rsa-pkcs1.key
# EC "traditionnel" (BEGIN EC PRIVATE KEY)
openssl ec -in ec256.key -out ec256-trad.key
# Courbes disponibles
openssl ecparam -list_curves
| Algorithme | Sécurité équivalente | Taille de signature | Compatibilité TLS |
| RSA 2048 | ~112 bits | 256 octets | Universelle |
| RSA 3072 | ~128 bits | 384 octets | Universelle, handshake plus lent |
| ECDSA P-256 | ~128 bits | ~72 octets | Tous les clients modernes |
| ECDSA P-384 | ~192 bits | ~104 octets | Tous les clients modernes |
| Ed25519 | ~128 bits | 64 octets | TLS 1.3 côté bibliothèques, mais quasi absent des AC publiques |
3. CSR avec SAN
3.1 En une ligne avec -addext
openssl req -new -key srv.key -out srv.csr \
-subj "/C=FR/O=Example/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:example.com,IP:192.0.2.10" \
-addext "keyUsage=critical,digitalSignature" \
-addext "extendedKeyUsage=serverAuth"
# Clé + CSR en une commande (-noenc remplace -nodes, déprécié en OpenSSL 3)
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \
-keyout srv.key -out srv.csr -subj "/CN=www.example.com" \
-addext "subjectAltName=DNS:www.example.com,DNS:example.com"
Ce que l'AC conserve : une AC publique ne reprend en général que la clé publique et les noms (validés) ; elle impose ses propres KU/EKU, durée et politique. Les champs O, OU, L ne sont repris que pour un certificat OV/EV (le champ OU est d'ailleurs interdit dans les certificats publics depuis 2022).
3.2 Avec un fichier de configuration (reproductible, versionnable)
# srv.cnf
[ req ]
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = req_ext
[ dn ]
C = FR
O = Example
CN = www.example.com
[ req_ext ]
subjectAltName = @alt_names
keyUsage = critical, digitalSignature
extendedKeyUsage = serverAuth
[ alt_names ]
DNS.1 = www.example.com
DNS.2 = example.com
DNS.3 = api.example.com
IP.1 = 192.0.2.10
openssl req -new -config srv.cnf -key srv.key -out srv.csr
openssl req -in srv.csr -noout -text -verify # contrôle : signature + extensions demandées
3.3 CSR depuis une clé ou un certificat existants
# Renouveler avec la même clé et les mêmes informations qu'un certificat existant
openssl x509 -x509toreq -in ancien.crt -signkey srv.key -copy_extensions copyall -out renouv.csr
# Certificat auto-signé rapide (lab, 30 jours)
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc -days 30 \
-keyout lab.key -out lab.crt -subj "/CN=lab.example.com" \
-addext "subjectAltName=DNS:lab.example.com"
Durée par défaut : sans -days, req -x509 et x509 -req émettent pour 30 jours. Et un certificat auto-signé n'a de sens que s'il est explicitement approuvé côté client ; pour plus de deux services, montez une AC de test (section suivante) ou une vraie PKI interne.
4. AC de test locale complète
Objectif : une racine, un intermédiaire, un certificat serveur et un certificat client, une CRL, puis un test de bout en bout avec s_server / s_client. Utile pour reproduire un problème de chaîne, tester du mTLS ou une configuration de proxy.
4.1 Fichier de configuration unique
# lab-ca.cnf
[ ca ]
default_ca = CA_inter
[ CA_inter ]
dir = ./db
database = $dir/index.txt
new_certs_dir = $dir/newcerts
serial = $dir/serial
crlnumber = $dir/crlnumber
certificate = ./inter.crt
private_key = ./inter.key
default_md = sha256
default_days = 90
default_crl_days = 7
policy = policy_any
copy_extensions = none
[ policy_any ]
commonName = supplied
[ req ]
distinguished_name = dn
prompt = no
[ dn ]
CN = placeholder
[ v3_root ]
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
[ v3_inter ]
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
crlDistributionPoints = URI:http://pki.lab.example.com/root.crl
[ v3_server ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
subjectAltName = DNS:www.lab.example.com, DNS:lab.example.com, IP:127.0.0.1
crlDistributionPoints = URI:http://pki.lab.example.com/inter.crl
[ v3_client ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
subjectAltName = email:svc-app@lab.example.com
4.2 Racine et intermédiaire
mkdir -p db/newcerts && touch db/index.txt
openssl rand -hex 16 > db/serial
echo 1000 > db/crlnumber
# Racine (10 ans)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out root.key
openssl req -x509 -new -config lab-ca.cnf -extensions v3_root -key root.key \
-subj "/O=Lab Example/CN=Lab Root CA" -days 3650 -sha384 -out root.crt
# Intermédiaire (5 ans), signé par la racine
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out inter.key
openssl req -new -config lab-ca.cnf -key inter.key -subj "/O=Lab Example/CN=Lab Issuing CA" -out inter.csr
openssl x509 -req -in inter.csr -CA root.crt -CAkey root.key -CAcreateserial \
-days 1825 -sha384 -extfile lab-ca.cnf -extensions v3_inter -out inter.crt
4.3 Certificats serveur et client
# Serveur
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out srv.key
openssl req -new -config lab-ca.cnf -key srv.key -subj "/CN=www.lab.example.com" -out srv.csr
openssl ca -batch -config lab-ca.cnf -extensions v3_server -in srv.csr -out srv.crt -notext
# Client (mTLS)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out client.key
openssl req -new -config lab-ca.cnf -key client.key -subj "/CN=svc-app" -out client.csr
openssl ca -batch -config lab-ca.cnf -extensions v3_client -in client.csr -out client.crt -notext
# Chaîne à servir : feuille puis intermédiaire (jamais la racine)
cat srv.crt inter.crt > srv.fullchain.pem
Pourquoi openssl ca pour les feuilles : il tient une base (index.txt) des certificats émis, ce qui permet la révocation et la génération de CRL. copy_extensions = none ignore les extensions de la CSR (le demandeur ne choisit pas ses SAN ni son EKU : c'est exactement la faille ESC1 d'ADCS transposée).
4.4 Révocation et CRL
openssl ca -config lab-ca.cnf -revoke client.crt -crl_reason keyCompromise
openssl ca -config lab-ca.cnf -gencrl -out inter.crl
openssl crl -in inter.crl -noout -text | head -20
# Vérifier avec contrôle de la CRL (feuille)
openssl verify -crl_check -CAfile root.crt -untrusted inter.crt -CRLfile inter.crl client.crt
# => error 23 at 0 depth lookup: certificate revoked
4.5 Test de bout en bout (TLS et mTLS)
# Terminal 1 : serveur de test, exige un certificat client
openssl s_server -accept 8443 -cert srv.crt -key srv.key -cert_chain inter.crt \
-CAfile root.crt -Verify 2 -www
# Terminal 2 : client
openssl s_client -connect 127.0.0.1:8443 -servername www.lab.example.com \
-CAfile root.crt -verify_hostname www.lab.example.com -verify_return_error \
-cert client.crt -key client.key -cert_chain inter.crt </dev/null
curl --cacert root.crt --cert client.crt --key client.key \
--resolve www.lab.example.com:8443:127.0.0.1 https://www.lab.example.com:8443/
-Verify 2 (majuscule) rend le certificat client obligatoire ; -verify 2 le demande sans l'exiger. -www renvoie une page d'état avec la session négociée.
5. Inspection de certificats, CSR, CRL
# Tout le contenu
openssl x509 -in cert.pem -noout -text
# Champs ciblés (beaucoup plus lisible dans un script)
openssl x509 -in cert.pem -noout -subject -issuer -serial -dates
openssl x509 -in cert.pem -noout -ext subjectAltName,keyUsage,extendedKeyUsage,basicConstraints
openssl x509 -in cert.pem -noout -ext authorityInfoAccess,crlDistributionPoints
openssl x509 -in cert.pem -noout -fingerprint -sha256
openssl x509 -in cert.pem -noout -enddate
openssl x509 -in cert.pem -noout -ocsp_uri
openssl x509 -in cert.pem -noout -nameopt RFC2253 -subject # format "CN=...,O=..."
# Expire dans moins de 30 jours ? (code retour 1 si oui)
openssl x509 -in cert.pem -noout -checkend 2592000
# Certificat DER
openssl x509 -in cert.der -inform DER -noout -text
# Tous les certificats d'un bundle PEM (fullchain, ca-bundle)
openssl storeutl -noout -text -certs bundle.pem | grep -E "^[0-9]+: Certificate|Subject:|Issuer:|Not After"
# alternative compatible 1.1.1
openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs -noout
# CSR
openssl req -in srv.csr -noout -text -verify
openssl req -in srv.csr -noout -subject
# CRL (souvent en DER)
openssl crl -in inter.crl -inform DER -noout -text
openssl crl -in inter.crl -inform DER -noout -lastupdate -nextupdate
# Structure ASN.1 brute (extension inconnue, débogage d'encodage)
openssl asn1parse -in cert.pem -i
# Empreinte SPKI (pinning, HPKP hérité, cert-manager, certaines API)
openssl x509 -in cert.pem -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | openssl base64
# Hash de sujet pour un répertoire de confiance (CApath)
openssl x509 -in root.crt -noout -subject_hash
openssl rehash /etc/ssl/mesac/
5.1 Lecture rapide d'un x509 -text
| Ligne | À vérifier |
Signature Algorithm | sha256WithRSAEncryption, ecdsa-with-SHA256/384 ; jamais SHA-1 / MD5 |
Issuer / Subject | Identiques = auto-signé (racine ou certificat de lab) |
Validity | Dates en UTC (GMT) ; comparer à l'horloge de la machine |
Public-Key | (2048 bit) / (256 bit) et la courbe NIST CURVE: P-256 |
X509v3 Subject Alternative Name | Tous les noms utilisés par les clients |
X509v3 Basic Constraints | CA:FALSE pour une feuille ; CA:TRUE, pathlen:0 pour un intermédiaire |
Authority Information Access | CA Issuers - URI (et OCSP s'il existe encore) |
CT Precertificate SCTs | Présents pour un certificat public |
6. Diagnostiquer un serveur avec s_client
# Connexion de base : chaîne envoyée, vérification, protocole, suite
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts </dev/null
# Résumé compact (protocole, suite, groupe, signataire, vérification)
openssl s_client -connect www.example.com:443 -servername www.example.com -brief </dev/null
# Échouer franchement si la chaîne ou le nom ne sont pas valides
openssl s_client -connect www.example.com:443 -servername www.example.com \
-verify_return_error -verify_hostname www.example.com </dev/null
# Utiliser une AC interne pour la vérification
openssl s_client -connect intranet.corp.example.com:443 -CAfile corp-root.pem </dev/null
# Réponse OCSP agrafée (stapling)
openssl s_client -connect www.example.com:443 -servername www.example.com -status </dev/null 2>/dev/null | grep -A 15 "OCSP response"
# ALPN (HTTP/2) et cible IP précise (un nœud derrière un LB)
openssl s_client -connect 192.0.2.21:443 -servername www.example.com -alpn h2,http/1.1 </dev/null | grep -i alpn
# Forcer IPv4 / IPv6, passer par un proxy HTTP CONNECT
openssl s_client -4 -connect www.example.com:443 </dev/null
openssl s_client -connect www.example.com:443 -proxy 10.0.0.3:3128 -servername www.example.com </dev/null
# Reprise de session (tickets / PSK TLS 1.3)
openssl s_client -connect www.example.com:443 -servername www.example.com -reconnect </dev/null | grep -E "^(New|Reused)"
# Journal des secrets pour déchiffrer une capture dans Wireshark (lab uniquement)
openssl s_client -connect www.example.com:443 -keylogfile /tmp/tls-keys.log </dev/null
# Trace détaillée de la négociation
openssl s_client -connect www.example.com:443 -servername www.example.com -msg -state </dev/null
6.1 Extraire les certificats du serveur
# Feuille seule
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null \
| openssl x509 -outform PEM > feuille.pem
# Toute la chaîne envoyée, un fichier par certificat (cert0.pem = feuille)
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts </dev/null 2>/dev/null \
| awk '/BEGIN CERT/{n++; f="cert" n-1 ".pem"} /BEGIN CERT/,/END CERT/{print > f}'
# Dates d'expiration de chaque certificat envoyé
for f in cert*.pem; do echo "$f : $(openssl x509 -in "$f" -noout -subject -enddate)"; done
6.2 Lire la sortie
| Ligne | Interprétation |
Certificate chain avec 0 s: / i: | Liste envoyée par le serveur : chaque i: doit être le s: de la ligne suivante |
Verify return code: 0 (ok) | Chaîne valide vers une racine du magasin utilisé |
20 (unable to get local issuer certificate) | Intermédiaire manquant ou racine absente du magasin |
21 (unable to verify the first certificate) | Le serveur n'envoie que la feuille |
10 (certificate has expired) | Un certificat de la chaîne est expiré (regarder la profondeur indiquée) |
18 / 19 (self-signed certificate (in certificate chain)) | Auto-signé, ou racine non approuvée |
62 (hostname mismatch) | Nom absent du SAN (avec -verify_hostname) |
Peer signature type, Negotiated TLS1.3 group | Algorithme de signature et groupe d'échange (X25519, X25519MLKEM768...) |
Secure Renegotiation IS NOT supported | Normal en TLS 1.3 |
Magasin de confiance : s_client utilise le magasin d'OpenSSL (OPENSSLDIR/certs), pas celui de Windows ni celui de Java. Sur Windows (Git Bash), un Verify return code: 20 peut simplement signifier qu'OpenSSL ne connaît pas la racine : passer -CAfile explicitement.
7. Vérifier une chaîne, une CRL, l'OCSP
# Chaîne : la racine dans -CAfile (ancre), les intermédiaires dans -untrusted
openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem
# Afficher le chemin construit
openssl verify -show_chain -CAfile root.pem -untrusted intermediate.pem leaf.pem
# Avec le magasin système (intermédiaires en -untrusted)
openssl verify -untrusted intermediate.pem leaf.pem
# Contrôler l'usage et le nom
openssl verify -purpose sslserver -verify_hostname www.example.com -CAfile root.pem -untrusted intermediate.pem leaf.pem
# Vérifier à une date donnée (epoch) : utile après un renouvellement d'intermédiaire
openssl verify -attime $(date -d "2027-01-15" +%s) -CAfile root.pem -untrusted intermediate.pem leaf.pem
# CRL : feuille seule (-crl_check) ou toute la chaîne (-crl_check_all)
curl -s -o inter.crl "$(openssl x509 -in leaf.pem -noout -ext crlDistributionPoints | grep -o 'http[^ ]*')"
openssl crl -in inter.crl -inform DER -out inter.crl.pem # les CRL publiées sont en général en DER
openssl verify -crl_check -CAfile root.pem -untrusted intermediate.pem -CRLfile inter.crl.pem leaf.pem
# OCSP (si l'AC en propose encore)
OCSP_URL=$(openssl x509 -in leaf.pem -noout -ocsp_uri)
openssl ocsp -issuer intermediate.pem -cert leaf.pem -url "$OCSP_URL" -resp_text -noverify | grep -E "Cert Status|This Update|Next Update"
-CAfile vs -untrusted : mettre l'intermédiaire dans -CAfile fait de lui une ancre de confiance : la vérification réussit même si la racine est inconnue ou expirée, et masque le problème. Seules les racines vont dans -CAfile. Avec OpenSSL 1.1.0+, -partial_chain rend ce comportement explicite.
8. Conversions de formats (PEM, DER, PKCS#7, PKCS#12, JKS)
8.1 PEM, DER, PKCS#7
# Certificat PEM <-> DER
openssl x509 -in cert.pem -outform DER -out cert.der
openssl x509 -in cert.der -inform DER -out cert.pem
# Clé PEM <-> DER
openssl pkey -in srv.key -outform DER -out srv.key.der
openssl pkey -in srv.key.der -inform DER -out srv.key
# CRL DER -> PEM
openssl crl -in ca.crl -inform DER -out ca.crl.pem
# PEM (chaîne) -> PKCS#7 (.p7b, import Windows/Java d'une chaîne)
openssl crl2pkcs7 -nocrl -certfile fullchain.pem -out chain.p7b
# PKCS#7 -> PEM (souvent livré par les AC commerciales et ADCS)
openssl pkcs7 -in chain.p7b -print_certs -out chain.pem
openssl pkcs7 -in chain.p7b -inform DER -print_certs -out chain.pem
8.2 PKCS#12 / PFX
# Créer un PFX : clé + certificat + chaîne, avec un nom convivial
openssl pkcs12 -export -inkey srv.key -in srv.crt -certfile inter.crt \
-name "www.example.com 2026" -out srv.pfx -passout file:./pfx.pass
# PFX compatible avec les anciens Windows (Server 2016 et antérieurs) et vieux Java :
# OpenSSL 3 chiffre par défaut en AES-256-CBC + PBKDF2 + MAC SHA-256, illisible pour eux
openssl pkcs12 -export -inkey srv.key -in srv.crt -certfile inter.crt -out srv-compat.pfx \
-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 -passout file:./pfx.pass
# Lire / inspecter un PFX (algorithmes utilisés, sans afficher la clé)
openssl pkcs12 -in srv.pfx -info -noout -passin file:./pfx.pass
# Vieux PFX (RC2-40, export Windows ancien) : erreur "unsupported" en OpenSSL 3
openssl pkcs12 -in ancien.pfx -legacy -info -noout
# ou explicitement
openssl pkcs12 -in ancien.pfx -provider legacy -provider default -info -noout
# Extraire
openssl pkcs12 -in srv.pfx -nocerts -noenc -out srv.key # clé en clair (protéger le fichier)
openssl pkcs12 -in srv.pfx -clcerts -nokeys -out srv.crt # certificat feuille
openssl pkcs12 -in srv.pfx -cacerts -nokeys -out chain.pem # intermédiaires
openssl pkcs12 -in srv.pfx -noenc -out tout.pem # tout (clé + certs)
# Re-chiffrer un vieux PFX au format moderne (en deux temps, sans fichier intermédiaire)
openssl pkcs12 -in ancien.pfx -legacy -noenc -passin file:./old.pass \
| openssl pkcs12 -export -out moderne.pfx -passout file:./pfx.pass
Mot de passe vide vs absent : -passout pass: crée un PFX avec un mot de passe vide (intégrité MAC calculée avec une chaîne vide) ; -nomac / -keypbe NONE créent un PFX réellement non protégé. Windows, Java et Azure ne réagissent pas de la même façon aux deux cas : certains outils refusent un mot de passe vide, d'autres exigent qu'il n'y en ait pas. En production, toujours un vrai mot de passe, transmis par un canal séparé.
8.3 Java : JKS et PKCS12 avec keytool
# PFX -> JKS (depuis Java 9, PKCS12 est le format par défaut : préférer garder le .p12)
keytool -importkeystore -srckeystore srv.p12 -srcstoretype PKCS12 \
-destkeystore srv.jks -deststoretype JKS
# JKS -> PKCS12 (puis openssl pour obtenir du PEM)
keytool -importkeystore -srckeystore ancien.jks -srcstoretype JKS \
-destkeystore ancien.p12 -deststoretype PKCS12
openssl pkcs12 -in ancien.p12 -noenc -out ancien.pem
# Truststore : ajouter une AC interne
keytool -importcert -alias corp-root -file corp-root.crt -keystore truststore.p12 -storetype PKCS12
keytool -importcert -alias corp-root -file corp-root.crt -cacerts # magasin global du JDK
# Lister
keytool -list -v -keystore srv.p12 -storetype PKCS12 | grep -E "Alias|Valid|Owner|Issuer"
8.4 Assembler les fichiers attendus par chaque logiciel
| Logiciel | Attendu | Commande |
| nginx | ssl_certificate = feuille + intermédiaires ; clé séparée | cat srv.crt inter.crt > fullchain.pem |
| Apache 2.4.8+ | SSLCertificateFile peut contenir la chaîne (SSLCertificateChainFile est obsolète) | Idem nginx |
| HAProxy | Un seul PEM : certificat, chaîne, clé | cat srv.crt inter.crt srv.key > /etc/haproxy/certs/www.pem |
| IIS / Windows / Azure | PFX avec chaîne | openssl pkcs12 -export ... |
| Tomcat / Java | PKCS12 (ou JKS) | openssl pkcs12 -export ... -name tomcat |
| Kubernetes Secret TLS | tls.crt = fullchain, tls.key | kubectl create secret tls www --cert=fullchain.pem --key=srv.key |
| Postfix / Dovecot | Fullchain + clé (ou fichier combiné clé d'abord pour Postfix smtpd_tls_chain_files) | Voir la documentation de la version |
9. Appariement clé / certificat / CSR
Méthode universelle (RSA, ECDSA, Ed25519) : comparer l'empreinte de la clé publique extraite de chaque objet. Les trois empreintes doivent être identiques.
openssl pkey -in srv.key -pubout -outform DER | openssl dgst -sha256
openssl x509 -in srv.crt -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256
openssl req -in srv.csr -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256
# Variante historique, RSA uniquement (modulus)
openssl rsa -in srv.key -noout -modulus | openssl sha256
openssl x509 -in srv.crt -noout -modulus | openssl sha256
# Fonction shell : retrouver la clé d'un certificat parmi un répertoire de clés
match_key() {
local crt="$1"; shift
local ref; ref=$(openssl x509 -in "$crt" -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 -r | cut -d' ' -f1)
for k in "$@"; do
h=$(openssl pkey -in "$k" -pubout -outform DER 2>/dev/null | openssl dgst -sha256 -r | cut -d' ' -f1)
[ "$h" = "$ref" ] && echo "MATCH : $k"
done
}
match_key srv.crt /etc/ssl/private/*.key
Intermédiaire attendu : pour savoir si inter.crt est bien l'émetteur de srv.crt, comparer l'Authority Key Identifier de la feuille au Subject Key Identifier de l'intermédiaire :
openssl x509 -in srv.crt -noout -ext authorityKeyIdentifier et openssl x509 -in inter.crt -noout -ext subjectKeyIdentifier, ou plus simplement openssl verify -partial_chain -CAfile inter.crt srv.crt.
10. Chiffrement, signature, hash et HMAC
10.1 Chiffrement symétrique de fichiers (enc)
# Chiffrer : AES-256-CBC, clé dérivée par PBKDF2 avec beaucoup d'itérations, sel aléatoire
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 600000 -in dump.sql -out dump.sql.enc -pass file:./backup.pass
# Déchiffrer : EXACTEMENT les mêmes options (-pbkdf2, -iter, algorithme)
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -in dump.sql.enc -out dump.sql -pass file:./backup.pass
# Sortie Base64 (copier-coller)
openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -a -in note.txt -out note.txt.b64 -pass file:./backup.pass
Limites de enc : sans
-pbkdf2, la clé est dérivée par
EVP_BytesToKey en une seule itération (attaque par dictionnaire triviale) et OpenSSL l'indique par un avertissement.
enc ne supporte pas les modes authentifiés (GCM) : un fichier altéré peut se déchiffrer en données corrompues sans erreur. Pour des sauvegardes, préférer
age, GnuPG, ou
openssl cms (ci-dessous) ; voir
Stratégie de sauvegarde.
10.2 Chiffrement asymétrique
# RSA brut : uniquement de petites données (< taille de clé - padding), OAEP obligatoire
openssl pkeyutl -encrypt -pubin -inkey dest.pub -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 \
-in secret.bin -out secret.bin.enc
openssl pkeyutl -decrypt -inkey dest.key -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 \
-in secret.bin.enc -out secret.bin
# Fichier de taille quelconque : CMS (chiffrement hybride, AES-256-GCM, destinataire = certificat)
openssl cms -encrypt -aes-256-gcm -recip dest.crt -binary -in gros.tar -outform DER -out gros.tar.cms
openssl cms -decrypt -inform DER -in gros.tar.cms -recip dest.crt -inkey dest.key -binary -out gros.tar
10.3 Signature et vérification
# RSA / ECDSA : signature d'un condensat SHA-256
openssl dgst -sha256 -sign signer.key -out release.tar.gz.sig release.tar.gz
openssl dgst -sha256 -verify signer.pub -signature release.tar.gz.sig release.tar.gz # "Verified OK"
# Ed25519 : pas de pré-hachage, utiliser pkeyutl -rawin
openssl pkeyutl -sign -inkey ed25519.key -rawin -in message.txt -out message.sig
openssl pkeyutl -verify -pubin -inkey ed25519.pub -rawin -in message.txt -sigfile message.sig
# Signature CMS/S-MIME détachée avec certificat (vérifiable par un tiers)
openssl cms -sign -signer signer.crt -inkey signer.key -certfile inter.crt -binary -in doc.pdf -outform DER -out doc.pdf.p7s
openssl cms -verify -inform DER -in doc.pdf.p7s -content doc.pdf -CAfile root.crt -binary -out /dev/null
10.4 Hash, HMAC, aléatoire, mots de passe
# Empreintes
openssl dgst -sha256 fichier.iso
openssl dgst -sha3-256 fichier.iso
openssl dgst -sha256 -r fichier.iso # format "hash *fichier" compatible sha256sum
# HMAC
openssl dgst -sha256 -hmac "$HMAC_KEY" payload.json
openssl mac -digest SHA256 -macopt hexkey:$(cat hmac.hex) -in payload.json HMAC # OpenSSL 3
# Vérifier une signature de webhook (exemple générique : HMAC-SHA256 en hexadécimal)
printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -r | cut -d' ' -f1
# Aléatoire cryptographique (secrets, jetons, clés)
openssl rand -hex 32
openssl rand -base64 48 | tr -d '\n'
# Hash de mot de passe pour /etc/shadow (SHA-512 crypt)
openssl passwd -6
# Base64 sans retour à la ligne / décodage
openssl base64 -A -in bin.dat
openssl base64 -d -in data.b64 -out data.bin
11. Tester protocoles et suites
11.1 Protocoles
# Le serveur accepte-t-il TLS 1.3 ? TLS 1.2 ?
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_3 </dev/null 2>&1 | grep -E "Protocol|Cipher is"
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2 </dev/null 2>&1 | grep -E "Protocol|Cipher is"
# Protocoles obsolètes : OpenSSL 3 les refuse au niveau de sécurité par défaut (SHA-1 dans le PRF)
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_1 -cipher 'DEFAULT:@SECLEVEL=0' </dev/null
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1 -cipher 'DEFAULT:@SECLEVEL=0' </dev/null
# "no protocols available" = c'est le CLIENT qui ne sait plus ; "alert protocol version" = le serveur refuse
11.2 Suites
TLS 1.2 et TLS 1.3 ont deux listes distinctes : -cipher (TLS ≤ 1.2, syntaxe OpenSSL) et -ciphersuites (TLS 1.3, noms IANA).
# Une suite TLS 1.2 précise
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2 \
-cipher 'ECDHE-ECDSA-AES128-GCM-SHA256' </dev/null 2>&1 | grep "Cipher is"
# Une suite TLS 1.3 précise
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_3 \
-ciphersuites 'TLS_CHACHA20_POLY1305_SHA256' </dev/null 2>&1 | grep "Cipher is"
# Groupes d'échange de clés (dont l'hybride post-quantique, OpenSSL 3.5+)
openssl s_client -connect www.example.com:443 -servername www.example.com -groups X25519MLKEM768 </dev/null 2>&1 | grep -i group
# Développer une chaîne de suites (avec noms IANA)
openssl ciphers -v -stdname -tls1_2 'ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL'
openssl ciphers -s -v -tls1_3
#!/usr/bin/env bash
# enum_ciphers.sh - suites TLS 1.2 et 1.3 acceptées par un serveur
H="${1:?hôte}"; P="${2:-443}"
for c in $(openssl ciphers 'ALL:COMPLEMENTOFALL:@SECLEVEL=0' | tr ':' ' '); do
if openssl s_client -connect "$H:$P" -servername "$H" -tls1_2 -cipher "$c:@SECLEVEL=0" </dev/null >/dev/null 2>&1; then
echo "TLS1.2 $c"
fi
done
for s in TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256; do
if openssl s_client -connect "$H:$P" -servername "$H" -tls1_3 -ciphersuites "$s" </dev/null >/dev/null 2>&1; then
echo "TLS1.3 $s"
fi
done
Outils complémentaires : nmap --script ssl-enum-ciphers -p 443 www.example.com (classe les suites de A à F),
testssl.sh (audit complet, vulnérabilités), et l'outil
Cipher Suite du site pour décoder une suite. Pour la configuration serveur cible, voir
SSL Labs et
Apache.
11.3 Signatures et certificats multiples
# Obtenir le certificat ECDSA d'un serveur qui sert RSA + ECDSA
openssl s_client -connect www.example.com:443 -servername www.example.com -sigalgs 'ECDSA+SHA256' </dev/null 2>/dev/null | openssl x509 -noout -text | grep "Public Key Algorithm"
openssl s_client -connect www.example.com:443 -servername www.example.com -sigalgs 'RSA-PSS+SHA256:RSA+SHA256' </dev/null 2>/dev/null | openssl x509 -noout -text | grep "Public Key Algorithm"
# Comportement sans SNI (certificat par défaut du serveur)
openssl s_client -connect 192.0.2.21:443 -noservername </dev/null 2>/dev/null | openssl x509 -noout -subject
12. STARTTLS et protocoles applicatifs
| Service | Port | Mode | Commande |
| SMTP soumission | 587 | STARTTLS | -starttls smtp |
| SMTP relais | 25 | STARTTLS | -starttls smtp -name relais.example.com |
| SMTPS | 465 | TLS direct | (aucune option) |
| IMAP / IMAPS | 143 / 993 | STARTTLS / direct | -starttls imap / direct |
| POP3 / POP3S | 110 / 995 | STARTTLS / direct | -starttls pop3 / direct |
| LDAP / LDAPS | 389 / 636 | StartTLS / direct | -starttls ldap / direct |
| PostgreSQL | 5432 | Négociation SSLRequest (ou TLS direct en PG 17+) | -starttls postgres (ou -alpn postgresql en TLS direct) |
| MySQL / MariaDB | 3306 | Négociation dans le handshake | -starttls mysql |
| FTP explicite | 21 | AUTH TLS | -starttls ftp |
| XMPP | 5222 | STARTTLS | -starttls xmpp -xmpphost example.com |
| RDP / SQL Server (TDS < 8) | 3389 / 1433 | TLS encapsulé | Non supporté par s_client : nmap --script ssl-cert |
# SMTP : certificat, protocole et EHLO du serveur
openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com -crlf -brief </dev/null
# Session SMTP interactive chiffrée (taper EHLO, puis QUIT)
openssl s_client -connect mail.example.com:587 -starttls smtp -crlf -quiet
# LDAP StartTLS (contrôleur de domaine) et LDAPS
openssl s_client -connect dc01.corp.example.com:389 -starttls ldap -showcerts </dev/null
openssl s_client -connect dc01.corp.example.com:636 -showcerts </dev/null
# PostgreSQL et MySQL : le certificat serveur est-il celui attendu ?
openssl s_client -connect db01.corp.example.com:5432 -starttls postgres </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate
openssl s_client -connect db02.corp.example.com:3306 -starttls mysql </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate
# Expiration de tous les services d'un serveur de messagerie
for spec in "587 smtp" "143 imap" "110 pop3" "993 -" "995 -" "465 -"; do
set -- $spec
opt=""; [ "$2" != "-" ] && opt="-starttls $2"
printf "%-5s " "$1"
openssl s_client -connect mail.example.com:$1 $opt -servername mail.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null || echo "KO"
done
SMTP entre serveurs : sur le port 25, la plupart des MTA acceptent un certificat non vérifiable (chiffrement opportuniste) sauf si MTA-STS ou DANE l'imposent. Un certificat expiré sur le MX peut donc passer inaperçu jusqu'à ce qu'un domaine partenaire publie MTA-STS en mode
enforce. Voir
Microsoft 365 / Exchange pour les connecteurs TLS.
# Opérations asymétriques par seconde (signatures = coût côté serveur TLS)
openssl speed -seconds 3 rsa2048 rsa3072 rsa4096 ecdsap256 ecdsap384 ed25519
# Chiffrement symétrique via EVP (utilise AES-NI / ARMv8 Crypto)
openssl speed -seconds 3 -evp aes-128-gcm
openssl speed -seconds 3 -evp aes-256-gcm
openssl speed -seconds 3 -evp chacha20-poly1305
# Tous les cœurs
openssl speed -multi $(nproc) -evp aes-256-gcm
# Mesurer le gain d'AES-NI (masque qui désactive AES-NI et PCLMULQDQ, x86_64)
OPENSSL_ia32cap="~0x200000200000000" openssl speed -seconds 3 -evp aes-128-gcm
# Échange de clés (OpenSSL 3.x : ecdh, et kem pour ML-KEM en 3.5+)
openssl speed -seconds 3 ecdhx25519 ecdhp256
# Handshakes par seconde contre un serveur réel
openssl s_time -connect www.example.com:443 -new -time 10 # sessions complètes
openssl s_time -connect www.example.com:443 -reuse -time 10 # avec reprise de session
| Constat typique | Interprétation | Action |
rsa2048 sign/s ≈ 10 à 20 fois plus lent que ecdsap256 sign/s | Le handshake RSA coûte cher en CPU serveur | Certificat ECDSA (ou double RSA + ECDSA) |
| AES-GCM > plusieurs Go/s par cœur | AES-NI actif | Rien à faire ; le chiffrement de données n'est pas le goulot |
| AES-GCM lent, ChaCha20 plus rapide | CPU sans accélération AES (ARM d'entrée de gamme, VM avec CPU masqué) | Vérifier le modèle de CPU exposé par l'hyperviseur (flag aes) |
-reuse >> -new | La reprise de session fonctionne | Partager les clés de tickets entre nœuds d'un cluster |
14. OpenSSL 3 : providers, legacy, FIPS
14.1 Les providers
| Provider | Contenu | Chargé par défaut |
default | Algorithmes modernes | Oui |
legacy | MD4, RC2, RC4, DES, Blowfish, CAST, IDEA, SEED, Whirlpool... | Non |
fips | Module validé FIPS 140 (sous-ensemble approuvé) | Non (activation explicite) |
base | Encodeurs/décodeurs sans algorithmes | Avec fips |
tiers (pkcs11, oqsprovider, tpm2) | HSM, TPM, algorithmes expérimentaux | Non |
# Activer ponctuellement
openssl list -providers
openssl list -cipher-algorithms -provider legacy -provider default | grep -i rc2
openssl pkcs12 -in ancien.pfx -provider legacy -provider default -info -noout
# Répertoire des modules et configuration
openssl version -m # MODULESDIR (legacy.so, fips.so)
export OPENSSL_MODULES=/usr/lib/x86_64-linux-gnu/ossl-modules
export OPENSSL_CONF=/etc/ssl/openssl-legacy.cnf
# openssl-legacy.cnf : default + legacy actifs pour une application
openssl_conf = openssl_init
[ openssl_init ]
providers = provider_sect
[ provider_sect ]
default = default_sect
legacy = legacy_sect
[ default_sect ]
activate = 1
[ legacy_sect ]
activate = 1
Ne pas activer legacy globalement : modifiez un fichier de configuration dédié et pointez-y une application via OPENSSL_CONF, plutôt que /etc/ssl/openssl.cnf. Activer le provider legacy pour tout le système réintroduit RC4 et DES partout.
14.2 Niveaux de sécurité (SECLEVEL)
| Niveau | Exigences principales | Usage |
| 0 | Aucune restriction | Tests de compatibilité uniquement |
| 1 (défaut amont) | RSA/DH ≥ 1024, ≥ 80 bits ; en OpenSSL 3, plus de SHA-1 en signature ni de TLS < 1.2 | Défaut |
| 2 | RSA/DH ≥ 2048, ≥ 112 bits | Défaut sur Debian/Ubuntu, RHEL (politiques crypto) |
| 3 | RSA/DH ≥ 3072, ≥ 128 bits, pas de reprise de session sans PFS | Environnements durcis |
Symptômes typiques : ca md too weak (certificat signé en SHA-1), ee key too small (RSA 1024), dh key too small. Sur RHEL/Fedora, update-crypto-policies --show donne la politique système (DEFAULT, LEGACY, FUTURE, FIPS) qui pilote OpenSSL, GnuTLS, NSS et Java.
14.3 FIPS
# OpenSSL compilé depuis les sources avec enable-fips : installer le module
openssl fipsinstall -out /usr/local/ssl/fipsmodule.cnf -module /usr/local/lib64/ossl-modules/fips.so
# openssl.cnf : inclure le module et l'activer
# .include /usr/local/ssl/fipsmodule.cnf
# [ provider_sect ]
# fips = fips_sect
# base = base_sect
# [ base_sect ]
# activate = 1
# [ algorithm_sect ]
# default_properties = fips=yes
openssl list -providers # doit afficher "fips" actif
openssl md5 /etc/hostname # doit échouer en mode FIPS strict
Distributions : sur RHEL et dérivées, le mode FIPS est une configuration du système entier (activé à l'installation ou par l'outil fourni selon la version), utilisant le module certifié de la distribution : ne pas recompiler OpenSSL soi-même dans ce cas. Seule une version précise du module, listée dans le certificat CMVP, est « validée FIPS ».
14.4 Changements de comportement 1.1.1 → 3.x
| Sujet | 1.1.1 | 3.x |
openssl rsa en sortie | PKCS#1 (BEGIN RSA PRIVATE KEY) | PKCS#8 (BEGIN PRIVATE KEY) ; -traditional pour PKCS#1 |
-nodes | Standard | Déprécié au profit de -noenc (toujours accepté) |
| PKCS#12 export | RC2-40 / 3DES, MAC SHA-1 | AES-256-CBC, PBKDF2, MAC SHA-256 |
| RC2, RC4, DES, MD4 | Disponibles | Provider legacy |
| TLS 1.0 / 1.1 | Disponibles | Seulement avec @SECLEVEL=0 |
| Renegociation non sécurisée | Tolérée côté client | Refusée (unsafe legacy renegotiation disabled) : option UnsafeLegacyServerConnect pour un vieux serveur |
| Engines | Mécanisme standard | Dépréciés, remplacés par les providers |
15. Équivalents Windows : certutil et PowerShell
| Besoin | OpenSSL | Windows natif |
| Afficher un certificat | openssl x509 -text -noout -in c.pem | certutil -dump c.cer |
| Vérifier chaîne + révocation | openssl verify -crl_check ... | certutil -verify -urlfetch c.cer |
| Hash d'un fichier | openssl dgst -sha256 f | Get-FileHash f -Algorithm SHA256 / certutil -hashfile f SHA256 |
| DER → PEM | openssl x509 -inform DER ... | certutil -encode c.der c.pem |
| PEM → DER | openssl x509 -outform DER ... | certutil -decode c.pem c.der |
| Créer une CSR | openssl req -new ... | certreq -new req.inf req.csr |
| Auto-signé | openssl req -x509 ... | New-SelfSignedCertificate |
| Exporter PFX | openssl pkcs12 -export ... | Export-PfxCertificate |
| Importer PFX | - | Import-PfxCertificate |
| Aléatoire | openssl rand -hex 32 | [Security.Cryptography.RandomNumberGenerator]::GetBytes(32) (PowerShell 7) |
| Tester un serveur TLS | openssl s_client | Script SslStream ci-dessous |
15.1 CSR avec certreq (clé générée dans le magasin machine)
; req.inf
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=www.example.com"
KeyAlgorithm = RSA
KeyLength = 3072
HashAlgorithm = SHA256
Exportable = FALSE
MachineKeySet = TRUE
ProviderName = "Microsoft Software Key Storage Provider"
RequestType = PKCS10
KeyUsage = 0xa0
[EnhancedKeyUsageExtension]
OID = 1.3.6.1.5.5.7.3.1
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=www.example.com&"
_continue_ = "dns=example.com"
certreq -new req.inf www.csr
# ... soumission à l'AC (ou ADCS : certreq -submit -config "srv-ca\Example Issuing CA 01" -attrib "CertificateTemplate:EX-ServeurWeb" www.csr www.cer)
certreq -accept www.cer # associe le certificat à la clé privée en attente
# Certificat sans clé après import : réassocier (sur la machine qui a généré la CSR)
certutil -repairstore my "<numéro_de_série>"
15.2 PowerShell
# Auto-signé ECDSA de lab, 90 jours
New-SelfSignedCertificate -DnsName "lab.example.com","192.0.2.10" -CertStoreLocation Cert:\LocalMachine\My `
-KeyAlgorithm ECDSA_nistP256 -HashAlgorithm SHA256 -NotAfter (Get-Date).AddDays(90) -KeyExportPolicy NonExportable
# Export / import PFX (mot de passe saisi, jamais en clair dans le script)
$pwd = Read-Host -AsSecureString "Mot de passe PFX"
Export-PfxCertificate -Cert Cert:\LocalMachine\My\<empreinte> -FilePath C:\temp\srv.pfx -Password $pwd -ChainOption BuildChain
Import-PfxCertificate -FilePath C:\temp\srv.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $pwd
# Exporter le certificat (sans clé) en PEM
$c = Get-Item Cert:\LocalMachine\My\<empreinte>
"-----BEGIN CERTIFICATE-----`n" + [Convert]::ToBase64String($c.RawData, 'InsertLineBreaks') + "`n-----END CERTIFICATE-----" |
Out-File -FilePath C:\temp\srv.pem -Encoding ascii
# PowerShell 7 : charger une paire PEM (cert + clé)
$x = [System.Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromPemFile("C:\temp\srv.crt", "C:\temp\srv.key")
$x.HasPrivateKey
# PowerShell 7 : équivalent minimal de s_client (protocole, suite, certificat)
$h = "www.example.com"
$tcp = [System.Net.Sockets.TcpClient]::new($h, 443)
# Callback qui accepte tout : INSPECTION uniquement, jamais dans un vrai client
$ssl = [System.Net.Security.SslStream]::new($tcp.GetStream(), $false, { $true })
$ssl.AuthenticateAsClient($h)
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]$ssl.RemoteCertificate
[pscustomobject]@{
Protocole = $ssl.SslProtocol
Suite = $ssl.NegotiatedCipherSuite
Sujet = $cert.Subject
Emetteur = $cert.Issuer
Expire = $cert.NotAfter
Empreinte = $cert.Thumbprint
}
$ssl.Dispose(); $tcp.Dispose()
# Validation de chaîne côté Windows (CryptoAPI, avec révocation)
$chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new()
$chain.ChainPolicy.RevocationMode = 'Online'
$chain.Build($cert); $chain.ChainStatus | Format-Table Status, StatusInformation
Protocoles Windows (SChannel) : les protocoles et suites acceptés par IIS, RDP ou WinRM sont réglés dans le registre SChannel et par
Get-TlsCipherSuite /
Disable-TlsCipherSuite, pas par OpenSSL. Voir
Windows Server et
Sécurité PowerShell.
16. Pièges et tableau récapitulatif
16.1 Pièges fréquents
| Symptôme | Cause | Correction |
unable to load certificate / no start line | Fichier DER lu comme PEM, ou BOM UTF-8 en début de fichier (édité sous Windows, Set-Content -Encoding UTF8 de PowerShell 5) | -inform DER ; retirer le BOM : sed -i '1s/^\xEF\xBB\xBF//' f.pem ; en PowerShell 5 écrire en ascii |
| Erreurs aléatoires de parsing, chaîne tronquée | Fins de ligne CRLF ou deux blocs collés (-----END CERTIFICATE----------BEGIN) après concaténation | sed -i 's/\r$//' f.pem ; vérifier que chaque fichier se termine par un saut de ligne avant cat |
| Navigateur OK, clients API KO | Ordre ou contenu de la chaîne : feuille absente en tête, intermédiaire manquant ou erroné | Ordre : feuille, intermédiaire(s) du plus bas au plus haut ; pas de racine |
key values mismatch (nginx), SSL_CTX_use_PrivateKey | Clé et certificat non appariés | Comparer les empreintes de clé publique (section 9) |
| Import PFX refusé sous un ancien Windows / Java : mot de passe incorrect | PFX OpenSSL 3 (AES/PBKDF2) non supporté | Réexporter en -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 |
unsupported ... RC2-40-CBC à la lecture d'un PFX | Vieux PFX, algorithme dans le provider legacy | -legacy |
| PFX accepté par un outil, refusé par un autre | Mot de passe vide vs absence de mot de passe | Toujours poser un mot de passe réel |
bad decrypt avec enc -d | Options différentes (-pbkdf2, -iter, -md : défaut MD5 avant 1.1.0, SHA-256 après) | Rejouer avec les options exactes du chiffrement ; essayer -md md5 pour un fichier ancien |
Firefox : SEC_ERROR_REUSED_ISSUER_AND_SERIAL | Numéro de série réutilisé par une AC de test (-set_serial 01) | -CAcreateserial ou série aléatoire ; supprimer l'exception dans le navigateur |
| Certificat valide 30 jours au lieu d'un an | -days oublié | Toujours préciser -days |
| Extensions absentes du certificat signé | x509 -req ignore les extensions de la CSR | -extfile/-extensions ou -copy_extensions copyall (en connaissance de cause) |
Mot de passe visible dans ps / l'historique | -passin pass:... | file:, env:, fd: |
Clé refusée par un vieux logiciel (expecting: ANY PRIVATE KEY, « format non supporté ») | PKCS#8 attendu en PKCS#1 ou inversement | openssl rsa -traditional / openssl pkey |
| Connexion vers une IP sans SNI : mauvais certificat | Pas de -servername (ou serveur sans vhost par défaut) | Toujours -servername ; tester -noservername pour voir le certificat par défaut |
s_client reste bloqué | Attente sur stdin | Rediriger </dev/null (ou -ign_eof / timeout 10) |
16.2 Tableau récapitulatif
| Besoin | Commande |
| Clé ECDSA P-256 | openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out k.key |
| Clé RSA 3072 | openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out k.key |
| Clé Ed25519 | openssl genpkey -algorithm ED25519 -out k.key |
| CSR avec SAN | openssl req -new -key k.key -subj "/CN=a.example.com" -addext "subjectAltName=DNS:a.example.com" -out r.csr |
| Auto-signé de lab | openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc -days 30 -keyout k -out c -subj "/CN=x" -addext "subjectAltName=DNS:x" |
| Signer une CSR | openssl x509 -req -in r.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 90 -extfile e.cnf -extensions v3_server -out c.crt |
| Lire un certificat | openssl x509 -in c.pem -noout -subject -issuer -dates -ext subjectAltName |
| Lire une CSR | openssl req -in r.csr -noout -text -verify |
| Chaîne d'un serveur | openssl s_client -connect h:443 -servername h -showcerts </dev/null |
| Expiration d'un serveur | openssl s_client -connect h:443 -servername h </dev/null 2>/dev/null | openssl x509 -noout -enddate |
| Expire sous N jours ? | openssl x509 -in c.pem -noout -checkend $((N*86400)) |
| Vérifier une chaîne | openssl verify -CAfile root.pem -untrusted inter.pem leaf.pem |
| Appariement | openssl pkey -in k -pubout -outform DER | openssl dgst -sha256 vs openssl x509 -in c -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 |
| PEM → DER | openssl x509 -in c.pem -outform DER -out c.der |
| P7B → PEM | openssl pkcs7 -in c.p7b -print_certs -out c.pem |
| PEM → PFX | openssl pkcs12 -export -inkey k -in c -certfile chain.pem -out f.pfx |
| PFX → PEM | openssl pkcs12 -in f.pfx -noenc -out all.pem (-legacy si ancien) |
| PFX → JKS | keytool -importkeystore -srckeystore f.p12 -srcstoretype PKCS12 -destkeystore k.jks -deststoretype JKS |
| Tester TLS 1.2 + suite | openssl s_client -connect h:443 -tls1_2 -cipher ECDHE-RSA-AES128-GCM-SHA256 |
| Tester TLS 1.3 + suite | openssl s_client -connect h:443 -tls1_3 -ciphersuites TLS_AES_256_GCM_SHA384 |
| STARTTLS SMTP | openssl s_client -connect h:587 -starttls smtp -crlf |
| Chiffrer un fichier | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 600000 -in f -out f.enc |
| Signer / vérifier | openssl dgst -sha256 -sign k -out s f / openssl dgst -sha256 -verify pub -signature s f |
| HMAC | openssl dgst -sha256 -hmac "$KEY" f |
| Aléatoire | openssl rand -hex 32 |
| Benchmark | openssl speed -evp aes-256-gcm ; openssl speed ecdsap256 rsa2048 |
| Providers | openssl list -providers |