Aller au contenu
ecdsa.com
← Tous les articles

OCSP et CRL en 2026 : comment se vérifie la révocation

La révocation des certificats a discrètement changé de forme. OCSP est facultatif et s’efface, les CRL font leur retour, les navigateurs consultent des listes qui leur sont transmises au lieu d’interroger les AC, et la plupart des clients HTTP ne vérifient toujours rien tant que vous ne le leur demandez pas. Chaque client présenté ci-dessous a été testé avec un certificat révoqué.

En bref. Depuis mars 2024, le CA/Browser Forum impose aux AC de publier des CRL et rend OCSP facultatif. Let’s Encrypt a retiré les URL OCSP de ses certificats en mai 2025 et a arrêté ses répondeurs en août 2025. Les navigateurs n’interrogent plus les AC sur chaque certificat. Firefox consulte un filtre CRLite local, Chrome le CRLSet qui lui est transmis, et Apple utilise sa propre liste agrégée. La plupart des bibliothèques et des outils en ligne de commande (Java, .NET, Go, Python, Node.js, curl) ne vérifient pas du tout la révocation par défaut. Tous ont accepté un certificat Let’s Encrypt révoqué lors de nos tests. Si votre client doit rejeter les certificats révoqués, activez explicitement la vérification des CRL et préparez-vous à des durées de vie de certificats courtes. C’est de toute façon dans cette direction que va le secteur.

Pourquoi la révocation devait être repensée

Un certificat peut être révoqué avant son expiration, généralement parce que sa clé a fuité ou qu’il a été émis par erreur. Il existait deux moyens classiques d’en informer les clients :

  • CRL (Certificate Revocation List, ou liste de révocation de certificats, RFC 5280) : l’AC publie une liste signée des numéros de série révoqués. Le certificat y renvoie dans son extension CRL Distribution Points.
  • OCSP (RFC 6960) : le client interroge le répondeur de l’AC sur un certificat précis et reçoit en retour un statut signé, « good » (non révoqué), « revoked » (révoqué) ou « unknown » (inconnu). L’URL figure dans l’extension Authority Information Access.

Les deux méthodes posaient des problèmes sur le Web. Les CRL pouvaient être volumineuses. OCSP révélait à l’AC chaque site visité par un utilisateur, ajoutait de la latence, et les navigateurs devaient se contenter d’un échec non bloquant (« soft-fail ») lorsque le répondeur était injoignable. Un attaquant capable de bloquer la requête OCSP rend alors la vérification inutile. L’agrafage OCSP (OCSP stapling) a résolu le problème de confidentialité en confiant au serveur le soin de récupérer la réponse et de la joindre au handshake, mais il ne s’est jamais généralisé. Must-Staple, un indicateur de certificat qui exige une réponse agrafée, n’a guère été pris en charge par les navigateurs.

Ce que disent les règles aujourd’hui

Le vote SC-063 (« Rendre OCSP facultatif, exiger les CRL et encourager l’automatisation », en vigueur depuis le 15 mars 2024) a modifié les Baseline Requirements :

  • Les CRL sont obligatoires. Une AC doit publier une CRL complète ou fragmentée dans les 24 heures qui suivent l’émission de son premier certificat. Elle doit mettre la CRL à jour au moins tous les 7 jours si tous ses certificats comportent une URL OCSP, tous les 4 jours dans le cas contraire, ainsi que dans les 24 heures qui suivent la révocation d’un certificat (§4.9.7). Pour les certificats d’abonné, nextUpdate peut être postérieur d’au plus 10 jours à thisUpdate (§7.2).
  • OCSP est facultatif. Si un certificat comporte une URL OCSP, le répondeur doit répondre, et la validité de ses réponses doit être comprise entre 8 heures et 10 jours (§4.9.9). Un certificat sans URL OCSP doit comporter un point de distribution de CRL (§7.1.2.11.2).
  • Les certificats de courte durée n’ont pas besoin d’informations de révocation. Un « Short-lived Subscriber Certificate » (certificat d’abonné de courte durée) est valable 7 jours au plus s’il est émis à partir du 15 mars 2026 (10 jours s’il a été émis avant cette date). L’AC peut en permettre la révocation, et le point de distribution de CRL est facultatif.
  • Délais de révocation. 24 heures en cas de compromission de clé et dans les cas similaires, 5 jours pour les autres (§4.9.1.1).

Let’s Encrypt a ensuite abandonné complètement OCSP, « principalement parce qu’il représente un risque considérable pour la confidentialité sur Internet » :

  • 30 janvier 2025 : les nouvelles demandes Must-Staple échouent.
  • 7 mai 2025 : les URL OCSP disparaissent des nouveaux certificats, qui comportent à la place des URL de CRL.
  • 6 août 2025 : les répondeurs OCSP sont arrêtés.

Ce que contiennent les certificats aujourd’hui

Vérifier sur quelques sites, le 20 octobre 2026, la présence d’une URL OCSP, d’une URL de CRL et d’une réponse OCSP agrafée donne un tableau contrasté :

ocsp-survey.sh, 20 octobre 2026 (OpenSSL 3.5.7)
letsencrypt.org      Let's Encrypt            OCSP=-                                    CRL=http://ye2.c.lencr.org/96.crl
    staple: OCSP response: no response sent
www.google.com       Google Trust Services    OCSP=-                                    CRL=http://c.pki.goog/we2/xuzt3PU9F_w.crl
    staple: OCSP response: no response sent
github.com           Sectigo Limited          OCSP=http://ocsp.sectigo.com              CRL=-
    staple: OCSP response: no response sent
www.digicert.com     DigiCert Inc             OCSP=http://ocsp.digicert.com             CRL=http://crl3.digicert.com/DigiCertEVRSACAG2.crl
    staple: OCSP Response Status: successful (0x0)
www.microsoft.com    Microsoft Corporation    OCSP=http://oneocsp.microsoft.com/ocsp    CRL=http://www.microsoft.com/pkiops/crl/partition/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2004_Partition00093.crl
    staple: OCSP Response Status: successful (0x0)
www.amazon.com       Amazon                   OCSP=http://ocsp.r2m04.amazontrust.com    CRL=http://crl.r2m04.amazontrust.com/r2m04.crl
    staple: OCSP Response Status: successful (0x0)

Let’s Encrypt et Google Trust Services émettent des certificats qui n’indiquent qu’une CRL. Le certificat de GitHub, émis par Sectigo, illustre le cas inverse, OCSP uniquement, ce que les BR autorisent. Un client qui ne prend en charge que l’une des deux méthodes ne peut pas vérifier tous ces certificats.

Les CRL ne sont pas petites, mais elles restent gérables :

Trois CRL réelles (20 octobre 2026)
http://yr1.c.lencr.org/46.crl                      192226 bytes    5094 entries  Oct 20 12:26:45 2026 GMT -> Oct 29 12:26:44 2026 GMT
http://c.pki.goog/we2/xuzt3PU9F_w.crl               87562 bytes    2456 entries  Oct 20 11:12:29 2026 GMT -> Oct 30 10:12:28 2026 GMT
http://crl3.digicert.com/DigiCertEVRSACAG2.crl     635286 bytes   17091 entries  Oct 20 06:24:08 2026 GMT -> Oct 27 06:24:08 2026 GMT

Let’s Encrypt fragmente ses CRL : chaque certificat pointe vers l’un des nombreux fichiers numérotés, et l’extension Issuing Distribution Point de la CRL désigne ce fragment. Le décodeur de CRL lit n’importe lequel de ces fichiers, vérifie sa signature avec le certificat de l’émetteur et vous indique si un certificat donné y figure.

Comment les navigateurs vérifient la révocation

Aucun des grands navigateurs n’envoie plus de requête OCSP pour un certificat ordinaire :

  • Firefox : CRLite. Firefox télécharge un encodage compressé de tous les certificats révoqués qui figurent dans les journaux CT, le met à jour toutes les 12 heures et le consulte localement. Cela représente en moyenne environ 300 Ko de données par jour, selon Mozilla. CRLite est activé pour tous les utilisateurs sur ordinateur depuis Firefox 137. Firefox 142 a cessé d’utiliser OCSP pour les certificats validés par domaine, et Firefox pour Android a adopté CRLite dans sa version 145.
  • Chrome : CRLSets. Chrome diffuse une liste construite à partir des CRL des AC, mais seulement « un sous-ensemble des certificats identifiés comme révoqués », et « Chrome n’effectue généralement pas de vérifications en ligne (c’est-à-dire OCSP et CRL) » (Chromium). Lors de notre test, Chromium 156 a chargé https://revoked-isrgrootx1.letsencrypt.org/ avec une réponse HTTP 200.
  • Apple : une liste agrégée. Apple collecte les données de révocation auprès des AC et les distribue aux appareils. En décembre 2025, le support technique d’Apple pour les développeurs a expliqué que seuls les certificats révoqués pour des motifs tels qu’une compromission de clé « finissent sur la liste “noire” d’Apple », et que les certificats Let’s Encrypt ne font l’objet d’aucune vérification OCSP secondaire. Un certificat révoqué pour un motif ordinaire peut donc toujours se charger dans Safari.

Concrètement, révoquer un certificat n’a rien d’un arrêt d’urgence immédiat dans les navigateurs. Firefox prend en compte les révocations grâce aux mises à jour CRLite effectuées toutes les 12 heures. Chrome et Safari peuvent ne jamais bloquer un certificat révoqué pour un motif ordinaire. La véritable limite pour une clé qui a fuité, c’est la durée de vie restante du certificat, et c’est l’une des raisons du passage aux certificats de 47 jours.

Ce que vérifient par défaut les bibliothèques et les outils

Chacun des clients ci-dessous s’est connecté le 20 octobre 2026 à https://revoked-isrgrootx1.letsencrypt.org/ (révoqué le 7 octobre 2026) et à https://valid-isrgrootx1.letsencrypt.org/. Le certificat révoqué comporte une URL de CRL et aucune URL OCSP.

ClientPar défaut, sur le site révoquéVérification de la révocation qui a fonctionné
Java 25 (JSSE)HTTP 200-Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true
.NET 10 HttpClientHTTP 200SslOptions.CertificateRevocationCheckMode = X509RevocationMode.Online
Go 1.27 crypto/tls200 OKVotre propre vérification dans tls.Config.VerifyConnection
Python 3.13 sslhandshake réussiVERIFY_CRL_CHECK_LEAF et la CRL chargée dans le contexte
Node.js 24 tlshandshake réussiOption crl avec une CRL par élément du tableau, pour chaque niveau de la chaîne
curl 8.14 (OpenSSL)HTTP 200--crlfile avec les CRL de chaque niveau de la chaîne
Chromium 156HTTP 200Aucune (CRLSet uniquement)

Vous trouverez ci-dessous les détails client par client.

Java

JSSE ne vérifie pas la révocation tant que com.sun.net.ssl.checkRevocation ne vaut pas true. Même dans ce cas, il n’utilise qu’OCSP, sauf si la prise en charge des CRL Distribution Points est elle aussi activée. C’est pourquoi un certificat Let’s Encrypt est rejeté comme inconnu et non comme révoqué :

JDK 25.0.4.1, revoked-isrgrootx1.letsencrypt.org
--- 1. default
HTTP 200
--- 2. -Dcom.sun.net.ssl.checkRevocation=true
javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path validation failed: java.security.cert.CertPathValidatorException: Could not determine revocation status
--- 3. checkRevocation + -Dcom.sun.security.enableCRLDP=true (revoked site)
javax.net.ssl.SSLHandshakeException: (certificate_revoked) PKIX path validation failed: java.security.cert.CertPathValidatorException: Certificate has been revoked, reason: UNSPECIFIED, revocation date: Wed Oct 07 18:37:19 UTC 2026, authority: CN=YR1, O=Let's Encrypt, C=US, extension OIDs: []
--- 4. same, valid site
HTTP 200

Lorsque la vérification est activée, le validateur PKIX échoue en mode bloquant s’il ne parvient pas à déterminer un statut. C’est prudent, mais une panne du serveur de CRL devient alors votre propre panne. java.security.cert.PKIXRevocationChecker permet de choisir, connexion par connexion, l’ordre entre OCSP et CRL ainsi que les options d’échec non bloquant (soft-fail).

.NET

HttpClient avec SocketsHttpHandler ne vérifie pas la révocation par défaut. Avec le mode Online, .NET récupère lui-même la CRL ou la réponse OCSP. Sous Linux, il le fait au moyen de sa propre implémentation reposant sur OpenSSL :

.NET 10 : vérification de la révocation pour un HttpClient
var handler = new SocketsHttpHandler();
handler.SslOptions.CertificateRevocationCheckMode = X509RevocationMode.Online;
using var client = new HttpClient(handler);
.NET SDK 10.0.402 sous Ubuntu 24.04
https://revoked-isrgrootx1.letsencrypt.org/ revocation=default -> 200
https://revoked-isrgrootx1.letsencrypt.org/ revocation=Online -> The remote certificate is invalid because of errors in the certificate chain: Revoked
https://valid-isrgrootx1.letsencrypt.org/ revocation=default -> 200
https://valid-isrgrootx1.letsencrypt.org/ revocation=Online -> 200

Go

crypto/tls vérifie la chaîne et le nom, mais jamais la révocation. Si vous en avez besoin, contrôlez vous-même la CRL une fois la vérification terminée. x509.ParseRevocationList et CheckSignatureFrom se chargent des parties délicates :

Go 1.27 : vérification de la CRL dans VerifyConnection
func checkCRL(cs tls.ConnectionState) error {
	chain := cs.VerifiedChains[0]
	leaf, issuer := chain[0], chain[1]
	if len(leaf.CRLDistributionPoints) == 0 {
		return errors.New("certificate has no CRL distribution point")
	}
	resp, err := http.Get(leaf.CRLDistributionPoints[0])
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	der, err := io.ReadAll(io.LimitReader(resp.Body, 64<<20))
	if err != nil {
		return err
	}
	crl, err := x509.ParseRevocationList(der)
	if err != nil {
		return err
	}
	if err := crl.CheckSignatureFrom(issuer); err != nil {
		return fmt.Errorf("CRL signature: %w", err)
	}
	if time.Now().After(crl.NextUpdate) {
		return errors.New("CRL is past its nextUpdate")
	}
	for _, entry := range crl.RevokedCertificateEntries {
		if entry.SerialNumber.Cmp(leaf.SerialNumber) == 0 {
			return fmt.Errorf("certificate %X revoked at %s", leaf.SerialNumber, entry.RevocationTime.UTC())
		}
	}
	return nil
}

// client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{VerifyConnection: checkCRL}}}
Go 1.27.3 : client par défaut, puis avec checkCRL
https://revoked-isrgrootx1.letsencrypt.org/ -> 200 OK
https://revoked-isrgrootx1.letsencrypt.org/ -> Get "https://revoked-isrgrootx1.letsencrypt.org/": certificate 67E51010C975E496C8EB800F3EF1BAB3F2B revoked at 2026-10-07 18:37:19 +0000 UTC
https://valid-isrgrootx1.letsencrypt.org/ -> 200 OK
https://valid-isrgrootx1.letsencrypt.org/ -> 200 OK

L’exemple ne vérifie que le certificat feuille et télécharge la CRL à chaque handshake. En production, mettez chaque CRL en cache jusqu’à son NextUpdate, et décidez de ce qui doit se passer lorsque le serveur de CRL est indisponible.

Python, Node.js et curl

Tous trois s’appuient sur la vérification des CRL d’OpenSSL et attendent que vous leur fournissiez les CRL. Ils ne les téléchargent jamais :

Python 3.13 (Debian 13) et Node.js 24.22.0, revoked-isrgrootx1.letsencrypt.org
python 3.13.5 default: handshake ok, TLSv1.3
python 3.13.5 VERIFY_CRL_CHECK_LEAF: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate revoked (_ssl.c:1029)
node v24.22.0 default: handshake ok
node v24.22.0 crl: UNABLE_TO_GET_CRL unable to get certificate CRL

L’option ssl.VERIFY_CRL_CHECK_LEAF de Python ne vérifie que le certificat feuille : il suffit donc de charger la CRL de ce certificat avec ctx.load_verify_locations(cafile=...). Node.js et curl vérifient chaque certificat de la chaîne, si bien qu’une CRL pour le seul certificat feuille donne UNABLE_TO_GET_CRL (Node) ou unable to get certificate CRL (curl), même sur un site valide. Fournissez-leur la CRL de chaque certificat de la chaîne. Pour Node.js 24, passez-les sous forme de tableau, avec une chaîne de caractères PEM par CRL. Lors de notre test, un tampon unique contenant les trois CRL concaténées échouait toujours avec UNABLE_TO_GET_CRL :

Node.js 24.22.0, les CRL des trois certificats de la chaîne
revoked-isrgrootx1.letsencrypt.org (one buffer): UNABLE_TO_GET_CRL unable to get certificate CRL
revoked-isrgrootx1.letsencrypt.org (array): CERT_REVOKED certificate revoked
valid-isrgrootx1.letsencrypt.org (one buffer): UNABLE_TO_GET_CRL unable to get certificate CRL
valid-isrgrootx1.letsencrypt.org (array): handshake ok

Les deux options de curl se comportent différemment. --cert-status exige une réponse OCSP agrafée : elle échoue donc sur tous les serveurs qui ne pratiquent pas l’agrafage, y compris tous les sites Let’s Encrypt. --crlfile fonctionne avec un fichier contenant les CRL de toute la chaîne :

curl 8.14.1 (OpenSSL 3.5.7)
$ curl -s -o /dev/null -w "HTTP %{http_code}\n" https://revoked-isrgrootx1.letsencrypt.org/
HTTP 200
$ curl -sS -o /dev/null --cert-status https://revoked-isrgrootx1.letsencrypt.org/
curl: (91) No OCSP response received
$ curl -sS -o /dev/null --crlfile revoked-chain-crls.pem https://revoked-isrgrootx1.letsencrypt.org/
curl: (60) SSL certificate problem: certificate revoked
$ curl -s -o /dev/null -w "HTTP %{http_code}\n" --crlfile valid-chain-crls.pem https://valid-isrgrootx1.letsencrypt.org/
HTTP 200
$ curl -sS -o /dev/null --crlfile leaf-crl-only.pem https://valid-isrgrootx1.letsencrypt.org/
curl: (60) SSL certificate problem: unable to get certificate CRL

Ce script récupère la CRL de chaque certificat envoyé par un serveur :

fetch-crls.sh HOST OUT : les CRL de toute la chaîne d’un serveur, au format PEM
#!/bin/bash
host="$1"; out="$2"
dir=$(mktemp -d)
openssl s_client -connect "$host:443" -servername "$host" -showcerts </dev/null 2>/dev/null \
  | awk -v d="$dir" '/BEGIN CERTIFICATE/{n++} n>0{print > (d "/cert" n ".pem")}'
: > "$out"
for cert in "$dir"/cert*.pem; do
  url=$(openssl x509 -in "$cert" -noout -ext crlDistributionPoints 2>/dev/null | grep -o 'http[^ ]*' | head -1)
  [ -n "$url" ] && curl -s "$url" | openssl crl -inform DER >> "$out"
done
rm -r "$dir"
echo "$host: $(grep -c 'BEGIN X509 CRL' "$out") CRLs"

Vérifier un certificat à la main

Avec OpenSSL, vous pouvez voir où se trouve le statut d’un certificat et le vérifier directement. Commencez par examiner les extensions :

Extensions du certificat de test révoqué (OpenSSL 3.5.7)
$ openssl x509 -in leaf.pem -noout -ext crlDistributionPoints,authorityInfoAccess
Authority Information Access: 
    CA Issuers - URI:http://yr1.i.lencr.org/
X509v3 CRL Distribution Points: 
    Full Name:
      URI:http://yr1.c.lencr.org/44.crl

Téléchargez ensuite la CRL et vérifiez le certificat par rapport à cette CRL. -untrusted fournit les certificats intermédiaires envoyés par le serveur :

openssl verify -crl_check (OpenSSL 3.5.7)
$ curl -s -o crl.der http://yr1.c.lencr.org/44.crl
$ openssl crl -inform DER -in crl.der -out crl.pem
$ openssl verify -crl_check -CRLfile crl.pem -untrusted chain.pem leaf.pem
CN=revoked-isrgrootx1.letsencrypt.org
error 23 at 0 depth lookup: certificate revoked
error leaf.pem: verification failed
$ openssl crl -in crl.pem -noout -text | grep -A1 "Serial Number: 067E51010C975E496C8EB800F3EF1BAB3F2B"
    Serial Number: 067E51010C975E496C8EB800F3EF1BAB3F2B
        Revocation Date: Oct  7 18:37:19 2026 GMT

Pour une AC qui exploite encore OCSP, interrogez directement le répondeur ou examinez la réponse agrafée :

openssl ocsp et s_client -status, www.digicert.com (sortie filtrée avec grep)
$ openssl ocsp -issuer issuer.pem -cert leaf.pem -url http://ocsp.digicert.com -resp_text -noverify
    OCSP Response Status: successful (0x0)
    Cert Status: good
    This Update: Oct 19 22:09:00 2026 GMT
    Next Update: Oct 26 21:09:00 2026 GMT
leaf.pem: good
	This Update: Oct 19 22:09:00 2026 GMT
	Next Update: Oct 26 21:09:00 2026 GMT
$ openssl s_client -connect www.digicert.com:443 -servername www.digicert.com -status </dev/null
    OCSP Response Status: successful (0x0)
    Cert Status: good

-noverify omet la vérification de la signature de la réponse, ce qui convient pour un rapide coup d’œil, mais pas pour prendre une décision. Le décodeur OCSP analyse les requêtes et les réponses, vérifie la signature du répondeur et son autorisation à répondre, et peut interroger un répondeur à votre place.

Que faire dans votre service

  • Demandez-vous si vous avez réellement besoin de vérifier la révocation. Pour les appels sortants vers des API publiques, le modèle des navigateurs (durées de vie courtes, CT, réponse des AC aux incidents) suffit généralement. Pour les connexions à forte valeur, comme les callbacks de paiement, le mTLS entre organisations ou la signature de code et de documents, vérifiez explicitement la révocation.
  • Prenez d’abord en charge les CRL. Les AC publiques doivent les publier, et certaines n’exploitent plus OCSP. Un client qui ne gère qu’OCSP ne pourra pas trancher pour les certificats de Let’s Encrypt et de Google Trust Services.
  • Mettez les CRL en cache et actualisez-les selon leur calendrier. Récupérez chaque CRL une seule fois, conservez-la jusqu’à nextUpdate et actualisez-la en arrière-plan, pour qu’un handshake TLS n’attende jamais un téléchargement.
  • Choisissez en connaissance de cause entre fail-open et fail-closed. En fail-closed (refus en cas d’échec), un problème de CDN chez l’AC devient votre panne. En fail-open (acceptation en cas d’échec), la vérification devient facultative pour quiconque peut bloquer le téléchargement de la CRL. Journalisez les deux issues.
  • Supprimez les dépendances à Must-Staple et à l’agrafage OCSP. Let’s Encrypt rejette les demandes Must-Staple, et l’agrafage n’est utile qu’avec les AC qui exploitent encore OCSP.
  • La PKI privée, c’est vous qui la concevez. Publiez des CRL avec un nextUpdate court, ou émettez des certificats de courte durée et renoncez à la révocation, comme les BR le permettent désormais pour les certificats de 7 jours ou moins.

Sources

Les tests ont été effectués le 20 octobre 2026 dans node:24.22.0-trixie (OpenSSL 3.5.7, curl 8.14.1, Node.js 24.22.0, Python 3.13.5), eclipse-temurin:25.0.4.1_1-jdk-noble, golang:1.27.3-trixie, mcr.microsoft.com/dotnet/sdk:10.0.402-noble et mcr.microsoft.com/playwright:v1.64.1-noble (Chromium 156).