CRL Scope Mismatch with OpenSSL Due to onlyContainsUserCerts in IDP Extension
Steps to Reproduce
- Start a step-ca instance (tested with v0.28.1) with CRL enabled in ca.json:
"crl": { "enabled": true, "cacheDuration": "1h0m0s", "refresh": "10m", "idpURL": "https://ca.example.local:8443/1.0/crl", "includeSubtree": true }
-
Issue a leaf certificate with CA:FALSE (e.g., a web server cert).
-
Revoke the certificate using:
step ca revoke --reason superseded
- Download the CRL (current.crl) and verify the certificate with OpenSSL:
openssl verify -crl_check -no_check_time
-CAfile intermediate-ca.pem
-CRLfile current.crl
cert.pem
Your Environment
- OS - Debian Linux v. 12
step-caVersion - v0.28.1
Expected Behavior
OpenSSL should validate the certificate as revoked, provided:
The CRL includes the revoked cert serial number.
The issuer of the CRL matches the issuer of the cert.
The cert has CA:FALSE (user cert).
The CRL's IDP does not impose conflicting constraints.
Actual Behavior
OpenSSL fails with:
error 44 at 0 depth lookup: different CRL scope error cert.pem: verification failed
The CRL contains the following extension:
X509v3 Issuing Distribution Point: critical Full Name: URI:https://ca.example.local:8443/1.0/crl Only User Certificates
Even though the revoked cert is a user cert (CA:FALSE), and the issuers match.
Additional Context
This issue appears to stem from OpenSSL's strict interpretation of the onlyContainsUserCerts flag. Despite the cert being a user cert, OpenSSL deems the CRL scope mismatched, likely due to the presence of the IDP extension.
Suggested Fix or Feature Request
Provide an option in step-ca to disable or customize the Issuing Distribution Point extension in CRLs (e.g., via ca.json).
Or remove the onlyContainsUserCerts flag unless explicitly required by a policy.
As OCSP is not available in the open-source edition, CRL behavior should be as broadly compatible as possible
Contributing
Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).
"Hi, just wondering if anyone had a chance to look at this yet? Happy to provide more info or help debug if needed."
I managed to resolve the issue by aligning the "Issuing Distribution Point" (IDP) in the metadata of the generated CRL with the "CRL Distribution Points" (CDP) present in the metadata of the leaf certificates — ensuring they both point to the same URL.
I also observed that the IDP URL embedded in the CRL is derived from the domain name configured in step-ca, which is correct. However, it does not take a custom port into account, and the resulting URL omits it. Despite this quirk, once the URLs were aligned, the “different CRL scope” error disappeared.
As a result, the following verification now behaves as expected:
openssl verify -crl_check -CAfile /etc/step-ca/certs/intermediate-ca2.crt -CRLfile current.crl /etc/ssl/certs/custom/peter.crt CN = peter.bullseye.local error 23 at 0 depth lookup: certificate revoked error /etc/ssl/certs/custom/peter.crt: verification failed
Even better, my Firefox Nightly instance — which has been configured to trust my custom root CA — now correctly flags the connection to the revoked host as insecure. Other certificates issued by the same CA that haven’t been revoked continue to be correctly recognized as secure.
I was wondering if aligning of IDP and CDP is a must or if this issue could have been addressed otherwise.
Regards, Michal
I have the same issue. Windows is reporting the CRL as invalid cause the flags says it only contains user certificates.
certutil -verify -urlfetch .\Downloads\Primary_CA.crt
Aussteller:
CN=Klett IT GmbH Primary Certification Authority
OU=IT Infrastructure and Operations
O=Klett IT GmbH
C=DE
Namenshash (sha1): 6c740bf2c5f9c525cc4efac2be0da9f8e6df6d19
Namenshash (md5): 474e297e3fb8422205c347b4434806e3
Antragsteller:
CN=K-OPS Primary Certification Authority
OU=IT Infrastructure and Operations
O=Klett IT GmbH
C=DE
Namenshash (sha1): e22e9a8218b808143f316d7cea0e7e77c9649454
Namenshash (md5): d8e91f575922485e3d786ca20d621143
Zertifikatseriennummer: 09443533d1b5b423df95b3c868ad8707
dwFlags = CA_VERIFY_FLAGS_CONSOLE_TRACE (0x20000000)
dwFlags = CA_VERIFY_FLAGS_DUMP_CHAIN (0x40000000)
ChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT (0x40000000)
HCCE_LOCAL_MACHINE
CERT_CHAIN_POLICY_BASE
-------- CERT_CHAIN_CONTEXT --------
ChainContext.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)
ChainContext.dwErrorStatus = CERT_TRUST_REVOCATION_STATUS_UNKNOWN (0x40)
ChainContext.dwErrorStatus = CERT_TRUST_IS_OFFLINE_REVOCATION (0x1000000)
SimpleChain.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)
SimpleChain.dwErrorStatus = CERT_TRUST_REVOCATION_STATUS_UNKNOWN (0x40)
SimpleChain.dwErrorStatus = CERT_TRUST_IS_OFFLINE_REVOCATION (0x1000000)
CertContext[0][0]: dwInfoStatus=102 dwErrorStatus=1000040
Issuer: CN=Klett IT GmbH Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
NotBefore: 15.04.2021 10:20
NotAfter: 11.04.2036 10:21
Subject: CN=K-OPS Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
Serial: 09443533d1b5b423df95b3c868ad8707
Cert: acd31a4298871b3934228290d750f820cfc2d623
Element.dwInfoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER (0x2)
Element.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)
Element.dwErrorStatus = CERT_TRUST_REVOCATION_STATUS_UNKNOWN (0x40)
Element.dwErrorStatus = CERT_TRUST_IS_OFFLINE_REVOCATION (0x1000000)
---------------- Zertifikat abrufen ----------------
ĂśberprĂĽft "Zertifikat (0)" Zeit: 0 94759ab59d72e156687e85a721dfb24e4f0fe133
[0.0] http://pki.services.klett-it.net/cert/Primary_CA.crt
---------------- Zertifikat abrufen ----------------
Keine IDP-Ăśberschneidung "Basissperrliste (75)" Zeit: 0 796474101edbb4ad1711d03a75b2ff2e9fe16413
[0.0] http://pki.services.klett-it.net/crl/Primary_CA.crl
---------------- Zertifikat-OCSP ----------------
Keine URLs "Keine" Zeit: 0 (null)
--------------------------------
CertContext[0][1]: dwInfoStatus=10c dwErrorStatus=0
Issuer: CN=Klett IT GmbH Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
NotBefore: 15.04.2021 09:54
NotAfter: 10.04.2041 09:54
Subject: CN=Klett IT GmbH Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
Serial: 77a7f399822525f255301e1746ea3d9a
Cert: 94759ab59d72e156687e85a721dfb24e4f0fe133
Element.dwInfoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER (0x4)
Element.dwInfoStatus = CERT_TRUST_IS_SELF_SIGNED (0x8)
Element.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)
---------------- Zertifikat abrufen ----------------
Keine URLs "Keine" Zeit: 0 (null)
---------------- Zertifikat abrufen ----------------
Keine URLs "Keine" Zeit: 0 (null)
---------------- Zertifikat-OCSP ----------------
Keine URLs "Keine" Zeit: 0 (null)
--------------------------------
Exclude leaf cert:
Chain: acd31a4298871b3934228290d750f820cfc2d623
Full chain:
Chain: 10e729a6fe106c39ca43512f1f3f8ff7996ca89c
Issuer: CN=Klett IT GmbH Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
NotBefore: 15.04.2021 10:20
NotAfter: 11.04.2036 10:21
Subject: CN=K-OPS Primary Certification Authority, OU=IT Infrastructure and Operations, O=Klett IT GmbH, C=DE
Serial: 09443533d1b5b423df95b3c868ad8707
Cert: acd31a4298871b3934228290d750f820cfc2d623
Die Sperrfunktion konnte die Sperrung nicht ĂĽberprĂĽfen, da der Sperrserver offline war. 0x80092013 (-2146885613 CRYPT_E_REVOCATION_OFFLINE)
certutil -dump .\Downloads\Primary_CA.crl
X.509-Zertifikatssperrliste:
Version: 2
Signaturalgorithmus:
Algorithmus Objekt-ID: 1.2.840.10045.4.3.2 sha256ECDSA
Algorithmusparameter: NULL
Aussteller:
CN=Klett IT GmbH Primary Certification Authority
OU=IT Infrastructure and Operations
O=Klett IT GmbH
C=DE
Namenshash (sha1): 6c740bf2c5f9c525cc4efac2be0da9f8e6df6d19
Namenshash (md5): 474e297e3fb8422205c347b4434806e3
Diese Aktualisierung: 20.05.2025 20:43
Nächste Aktualisierung: 21.05.2025 20:43
Einträge der Sperrliste: 0
Erweiterung der Sperrliste: 3
2.5.29.35: Kennzeichen = 0, Länge = 18
StellenschlĂĽsselkennung
SchlĂĽssel-ID=6cd141add13516b91bd20f51a7ea7bf4dd0b5395
2.5.29.20: Kennzeichen = 0, Länge = 3
Sperrlistennummer
Sperrlistennummer=75
2.5.29.28: Kennzeichen = 1(Kritisch), Länge = 3e
Ausstellender Verteilungspunkt
Name des Verteilungspunktes:
Vollst. Name:
URL=http://pki.services.klett-it.net/crl/Primary_CA.crl
Enthält nur Benutzerzertifikate=Ja <--- That is the problem here
Enthält nur Zertifizierungsstellenzertifikate=Nein
Indirekte Sperrliste=Nein
Signaturalgorithmus:
Algorithmus Objekt-ID: 1.2.840.10045.4.3.2 sha256ECDSA
Algorithmusparameter: NULL
Signatur: Nicht verwendete Bits=0
0000 75 74 9d cb 61 9c f4 13 a2 ef d0 e8 7b 60 1e 0e
0010 ef 19 29 37 39 5f 73 5a 3e 1c 36 99 ad 05 73 15
0020 20 02 8f 1b 37 24 20 3c 45 6a aa c9 66 e9 c8 5d
0030 59 a3 f1 b8 77 8d 91 7b df a1 b1 17 c0 2c 80 67
0040 11 24 20 02 44 30
Sperrlistenhash(md5): 3c091d22254de05f4142983cd627e700
Sperrlistenhash(sha1): 796474101edbb4ad1711d03a75b2ff2e9fe16413
Sperrlistenhash(sha256): f5218487d929ab105a4ceb657bb36b514b965632f2b7a35889b4b4065280defc
Signaturhash: 1083327d7ddc7e473c313c784fad0eb402e62f90037696cb3432e3beea3925c9
CertUtil: -dump-Befehl wurde erfolgreich ausgefĂĽhrt.
@miso-simo I've looked at this, and as you mention, the solution is be configure the templates with crlDistributionPoints:
{
"subject": {{ toJson .Subject }},
"sans": {{ toJson .SANs }},
{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }}
"keyUsage": ["keyEncipherment", "digitalSignature"],
{{- else }}
"keyUsage": ["digitalSignature"],
{{- end }}
"extKeyUsage": ["serverAuth", "clientAuth"],
"crlDistributionPoints": ["https://ca.example.local:8443/1.0/crl"]
}
In the openssl command you will also need to use a bundle with the root and intermediate certificates, it might work if you have the root installed in your system. This should work even if you don't:
curl -s https://ca.example.local:8443/1.0/roots.pem > ca.pem
curl -s https://ca.example.local:8443/1.0/intermediates.pem >> ca.pem
openssl verify -crl_check -no_check_time \
-CAfile ca.pem \
-CRLfile current.crl \
cert.pem
At this moment, we don't add the crlDistributionPoints to the issued certificates, but the users can add it with a custom template. In the ca.json it would be something like this:
{
"..." : "...",
"provisioners" [{
"type": "...",
"name": "...",
"...": "...",
"options": {
"x509": {
"templateFile": "/path/to/leaf.tpl"
}
}
}]
}