certificates icon indicating copy to clipboard operation
certificates copied to clipboard

CRL Scope Mismatch with OpenSSL Due to onlyContainsUserCerts in IDP Extension

Open miso-simo opened this issue 8 months ago • 4 comments

Steps to Reproduce

  1. 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 }

  1. Issue a leaf certificate with CA:FALSE (e.g., a web server cert).

  2. Revoke the certificate using:

step ca revoke --reason superseded

  1. 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-ca Version - 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).

miso-simo avatar Apr 23 '25 21:04 miso-simo

"Hi, just wondering if anyone had a chance to look at this yet? Happy to provide more info or help debug if needed."

miso-simo avatar Apr 27 '25 07:04 miso-simo

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

miso-simo avatar May 07 '25 22:05 miso-simo

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.

Seji64 avatar May 21 '25 08:05 Seji64

@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"
      }
    }
  }]
}

maraino avatar Jul 15 '25 02:07 maraino