Pular para o conteúdo
ecdsa.com
← Todos os artigos

OCSP e CRL em 2026: como se verifica a revogação hoje

A revogação de certificados mudou de forma sem alarde. O OCSP é opcional e está desaparecendo, as CRLs voltaram, os navegadores consultam listas distribuídas a eles em vez de perguntar às CAs, e a maioria dos clientes HTTP continua sem verificar nada, a menos que você peça. Cada cliente abaixo foi testado com um certificado revogado.

Resposta curta. Desde março de 2024, o CA/Browser Forum exige que as CAs publiquem CRLs e torna o OCSP opcional. A Let’s Encrypt removeu as URLs de OCSP dos seus certificados em maio de 2025 e desligou seus respondedores em agosto de 2025. Os navegadores não perguntam mais às CAs sobre cada certificado. O Firefox consulta um filtro CRLite local, o Chrome consulta o CRLSet distribuído a ele, e a Apple usa sua própria lista agregada. A maioria das bibliotecas e ferramentas de linha de comando (Java, .NET, Go, Python, Node.js, curl) não verifica revogação alguma por padrão. Nos nossos testes, todas aceitaram um certificado revogado da Let’s Encrypt. Se seu cliente precisa rejeitar certificados revogados, ative explicitamente a verificação de CRL e prepare-se para certificados com vida útil curta. É para lá que o setor está indo, de qualquer forma.

Por que a revogação precisou ser repensada

Um certificado pode ser revogado antes de expirar, em geral porque a chave vazou ou porque ele foi emitido por engano. As duas formas clássicas de avisar os clientes eram:

  • CRL (Certificate Revocation List, lista de certificados revogados, RFC 5280): a CA publica uma lista assinada com os números de série revogados. O certificado aponta para ela na extensão CRL Distribution Points.
  • OCSP (RFC 6960): o cliente pergunta ao respondedor da CA sobre um certificado e recebe uma resposta assinada: “good”, “revoked” ou “unknown”. A URL fica na extensão Authority Information Access.

Na web, os dois tinham problemas. As CRLs podiam ser grandes. O OCSP revelava à CA cada site que o usuário visitava, aumentava a latência, e os navegadores precisavam seguir em frente sem resposta (“soft-fail”) quando o respondedor estava inacessível. Um atacante capaz de bloquear a requisição OCSP torna a verificação inútil. O OCSP stapling resolveu o problema de privacidade fazendo o próprio servidor buscar a resposta e anexá-la, mas nunca se tornou universal. O Must-Staple, uma flag no certificado que exige uma resposta entregue por stapling, teve pouco suporte nos navegadores.

O que as regras dizem agora

A votação SC-063 (“Tornar o OCSP opcional, exigir CRLs e incentivar a automação”, em vigor desde 15 de março de 2024) mudou os Baseline Requirements:

  • As CRLs são obrigatórias. Uma CA deve publicar uma CRL completa ou fragmentada (sharded) em até 24 horas depois de emitir seu primeiro certificado. Ela deve atualizar a CRL pelo menos a cada 7 dias se todos os seus certificados trouxerem uma URL de OCSP, e a cada 4 dias caso contrário, além de atualizá-la em até 24 horas depois de revogar um certificado (§4.9.7). Para certificados de assinante, o nextUpdate pode ficar no máximo 10 dias depois do thisUpdate (§7.2).
  • O OCSP é opcional. Se um certificado tem uma URL de OCSP, o respondedor deve responder, e as respostas devem ter validade de 8 horas a 10 dias (§4.9.9). Um certificado sem URL de OCSP deve trazer um CRL Distribution Point (§7.1.2.11.2).
  • Certificados de curta duração dispensam informações de revogação. Um “Short-lived Subscriber Certificate” vale por no máximo 7 dias se for emitido a partir de 15 de março de 2026 (antes, 10 dias). A CA pode oferecer a revogação desse certificado, e o CRL Distribution Point é opcional.
  • Prazos de revogação. 24 horas para comprometimento da chave e casos semelhantes, 5 dias para os demais (§4.9.1.1).

Em seguida, a Let’s Encrypt abandonou o OCSP por completo, “principalmente porque ele representa um risco considerável à privacidade na internet”:

  • 30 de janeiro de 2025: novas solicitações com Must-Staple falham.
  • 7 de maio de 2025: as URLs de OCSP são removidas dos novos certificados, que passam a trazer URLs de CRL.
  • 6 de agosto de 2025: os respondedores OCSP são desligados.

O que os certificados trazem hoje

Verificar alguns sites em 20 de outubro de 2026 em busca de uma URL de OCSP, uma URL de CRL e uma resposta OCSP por stapling mostra um quadro misto:

ocsp-survey.sh, 20 de outubro de 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)

A Let’s Encrypt e a Google Trust Services emitem certificados só com CRL. O certificado do GitHub, emitido pela Sectigo, é o caso inverso: só OCSP, o que os BR permitem. Um cliente que aceita apenas um dos dois métodos não consegue verificar todos esses certificados.

As CRLs não são pequenas, mas têm um tamanho administrável:

Três CRLs reais (20 de outubro de 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

A Let’s Encrypt fragmenta suas CRLs: cada certificado aponta para um entre muitos arquivos numerados, e a extensão Issuing Distribution Point da CRL identifica esse fragmento. O decodificador de CRL lê qualquer um desses arquivos, confere a assinatura da CRL com o emissor e informa se um determinado certificado está nela.

Como os navegadores verificam a revogação

Nenhum dos principais navegadores envia mais requisições OCSP para um certificado comum:

  • Firefox: CRLite. O Firefox baixa uma codificação compactada de todos os certificados revogados que aparecem nos logs CT, atualiza esses dados a cada 12 horas e os consulta localmente. Segundo a Mozilla, isso representa cerca de 300 kB de dados por dia, em média. O CRLite está ativado para todos os usuários de desktop desde o Firefox 137. O Firefox 142 deixou de usar OCSP para certificados com validação de domínio, e o Firefox para Android adotou o CRLite na versão 145.
  • Chrome: CRLSets. O Chrome distribui uma lista montada a partir das CRLs das CAs, mas só com “um subconjunto dos certificados identificados como revogados”, e “verificações online (isto é, OCSP e CRL) geralmente não são feitas pelo Chrome” (Chromium). No nosso teste, o Chromium 156 carregou https://revoked-isrgrootx1.letsencrypt.org/ com HTTP 200.
  • Apple: uma lista agregada. A Apple coleta dados de revogação das CAs e os distribui aos dispositivos. Em dezembro de 2025, o suporte técnico para desenvolvedores da Apple explicou que os certificados “só vão parar na lista de ‘malcomportados’ da Apple” quando são revogados por motivos como comprometimento da chave, e que não há uma verificação OCSP secundária para certificados da Let’s Encrypt. Por isso, o Safari ainda pode aceitar um certificado revogado por um motivo comum.

Na prática, isso significa que revogar um certificado não funciona como um botão de emergência rápido nos navegadores. O Firefox recebe as revogações pelas atualizações do CRLite a cada 12 horas. O Chrome e o Safari podem nunca chegar a bloquear uma revogação comum. O limite real para uma chave vazada é a vida útil restante do certificado, e esse é um dos motivos da mudança para certificados de 47 dias.

O que bibliotecas e ferramentas verificam por padrão

Em 20 de outubro de 2026, cada cliente abaixo se conectou a https://revoked-isrgrootx1.letsencrypt.org/ (revogado em 7 de outubro de 2026) e a https://valid-isrgrootx1.letsencrypt.org/. O certificado revogado tem uma URL de CRL e nenhuma URL de OCSP.

ClientePadrão no site revogadoVerificação de revogação que funcionou
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 OKVerificação própria em tls.Config.VerifyConnection
Python 3.13 sslhandshake okVERIFY_CRL_CHECK_LEAF e a CRL carregada no contexto
Node.js 24 tlshandshake okOpção crl com uma CRL por elemento do array, para cada nível da cadeia
curl 8.14 (OpenSSL)HTTP 200--crlfile com as CRLs de cada nível da cadeia
Chromium 156HTTP 200Nenhuma (só o CRLSet)

Os detalhes de cada cliente vêm a seguir.

Java

O JSSE não verifica revogação a menos que com.sun.net.ssl.checkRevocation seja true. Mesmo assim, ele usa só OCSP, a não ser que os CRL Distribution Points também estejam habilitados, e é por isso que um certificado da Let’s Encrypt falha como desconhecido, e não como revogado:

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

Com a verificação ativada, o validador PKIX rejeita o certificado (hard-fail) quando não consegue determinar um status. Isso é seguro, mas aí uma queda do servidor de CRL vira uma queda do seu serviço. O java.security.cert.PKIXRevocationChecker permite escolher, por conexão, a ordem entre OCSP e CRL e as opções de soft-fail.

.NET

O HttpClient com SocketsHttpHandler não verifica revogação por padrão. Definir o modo como Online faz o .NET buscar por conta própria a CRL ou a resposta OCSP. No Linux, ele faz isso com uma implementação própria sobre o OpenSSL:

.NET 10: verificação de revogação para um HttpClient
var handler = new SocketsHttpHandler();
handler.SslOptions.CertificateRevocationCheckMode = X509RevocationMode.Online;
using var client = new HttpClient(handler);
.NET SDK 10.0.402 no 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

O crypto/tls valida a cadeia e o nome e nunca verifica a revogação. Se você precisar disso, confira a CRL por conta própria depois da verificação. x509.ParseRevocationList e CheckSignatureFrom fazem a parte difícil:

Go 1.27: verificação da CRL em 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: cliente padrão e depois com 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

O exemplo verifica só o certificado folha e baixa a CRL a cada handshake. Em produção, mantenha cada CRL em cache até o NextUpdate dela e decida o que acontece quando o servidor de CRL estiver fora do ar.

Python, Node.js e curl

Os três usam a verificação de CRL do OpenSSL e esperam que você forneça as CRLs. Eles nunca as baixam:

Python 3.13 (Debian 13) e 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

O ssl.VERIFY_CRL_CHECK_LEAF do Python verifica só o certificado folha, então basta carregar a CRL dele com ctx.load_verify_locations(cafile=...). O Node.js e o curl verificam cada certificado da cadeia, então uma CRL só do certificado folha resulta em UNABLE_TO_GET_CRL (Node) ou unable to get certificate CRL (curl) mesmo em um site válido. Forneça a CRL de cada certificado da cadeia. No Node.js 24, passe as CRLs como um array com uma string PEM por CRL. No nosso teste, um único buffer com três CRLs concatenadas ainda falhou com UNABLE_TO_GET_CRL:

Node.js 24.22.0, as CRLs dos três certificados da cadeia
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

As duas opções do curl se comportam de formas diferentes. A opção --cert-status exige uma resposta OCSP entregue por stapling, então falha em todo servidor que não faz stapling, incluindo todos os sites da Let’s Encrypt. A opção --crlfile funciona com um arquivo que contém as CRLs de toda a cadeia:

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

Este script reúne a CRL de cada certificado que um servidor envia:

fetch-crls.sh HOST OUT: as CRLs de toda a cadeia de um servidor, em 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"

Como verificar um certificado manualmente

Com o OpenSSL, você pode ver onde fica o status de um certificado e verificá-lo diretamente. Comece pelas extensões:

Extensões do certificado de teste revogado (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

Depois, baixe a CRL e verifique o certificado com ela. A opção -untrusted fornece os intermediários que o servidor enviou:

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

Para uma CA que ainda mantém OCSP, pergunte diretamente ao respondedor ou examine a resposta entregue por stapling:

openssl ocsp e s_client -status, www.digicert.com (saída filtrada com 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

A opção -noverify pula a verificação da assinatura da resposta, o que serve para uma olhada rápida, mas não para tomar uma decisão. O decodificador OCSP analisa requisições e respostas, verifica a assinatura e a autorização do respondedor e pode consultar um respondedor por você.

O que fazer no seu serviço

  • Decida se você precisa mesmo de verificação de revogação. Para chamadas de saída a APIs públicas, o modelo dos navegadores (vidas úteis curtas, CT, resposta a incidentes por parte das CAs) costuma bastar. Para conexões de alto valor, como callbacks de pagamento, mTLS entre organizações ou assinatura de código e de documentos, verifique a revogação explicitamente.
  • Priorize o suporte a CRLs. As CAs públicas são obrigadas a publicá-las, e algumas não mantêm mais OCSP. Um cliente que só faz OCSP não vai conseguir decidir nada sobre certificados da Let’s Encrypt e da Google Trust Services.
  • Mantenha as CRLs em cache e atualize-as no ritmo delas. Baixe cada CRL uma vez, guarde-a até o nextUpdate e atualize-a em segundo plano, para que um handshake TLS nunca espere por um download.
  • Escolha entre fail-open e fail-closed de forma consciente. O fail-closed transforma um problema na CDN de uma CA em uma queda do seu serviço. O fail-open torna a verificação opcional para qualquer um que consiga bloquear o download da CRL. Registre em log os dois desfechos.
  • Elimine dependências de Must-Staple e de OCSP stapling. A Let’s Encrypt rejeita solicitações com Must-Staple, e o stapling só ajuda com CAs que ainda mantêm OCSP.
  • Na PKI privada, quem decide o projeto é você. Publique CRLs com um nextUpdate curto, ou emita certificados de curta duração e dispense a revogação, como os BR agora permitem para certificados de 7 dias ou menos.

Fontes

Os testes rodaram em 20 de outubro de 2026 em 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 e mcr.microsoft.com/playwright:v1.64.1-noble (Chromium 156).