smallstep / certificates

🛡️ A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH.
https://smallstep.com/certificates
Apache License 2.0
6.73k stars 440 forks source link

Add Support for AIA / caIssuers Extension #1144

Closed foleyjohnm closed 1 year ago

foleyjohnm commented 2 years ago

Hello!

Issue details

It would be great if there was support for adding the Authority Information Access (AIA, 1.3.6.1.5.5.7.1.1) extension to certificates issued off of a step-ca. I am mainly looking to add caIssuers, but OCSP would be nice too.

@hslatman and I sort of discussed it on Discord, and generating this asn1 structure is painful. What I ended up doing (at @hslatman 's suggestion) was creating a dummy configuration in openssl that had the AIA values I wanted, and issuing a cert. Then, I used this asn1 decoder, https://lapo.it/asn1js (or, you could use openssl asn1parse) to pull the hex bytes of the sequence INSIDE the AIA sequence. Converted those to b64 and used those in the step template file: { "extensions": [ {"id": "1.3.6.1.5.5.7.1.1", "critical": false, "value": "MCYwJAYIKwYBBQUHMAKGGGh0dHA6Ly9jcmwubG9jYWwvaW50LmNydA=="} ] }

That results in the issued certificates having the AIA extensions:

Authority Information Access: CA Issuers - URI:http://crl.local/int.crt

Why is this needed?

It could certainly help with certificate chaining issues and to avoid adding having to specifically trust the step intermediate CA.

For example, this is what my environment looks like Offline Root CA (ADCS in standalone mode) Step CA (Intermediate cert issued by my root)

In my enterprise environment, everything trusts the root CA (devices, servers, workstations), and with the AIA extension, I can get more built in trust. Otherwise, I would have to import the intermediate in AD or a GPO, distribute it to devices, etc. That is a good best practice anyway....

ie. With no AIA extension, servers using a Step CA from the intermediate come up as unknown, as Windows has no idea about the intermediate CA

With the AIA extension configured correctly, Windows is able to chain the certificate up to the root, and it has a valid path.

maraino commented 2 years ago

This one is actually easy to add with issuingCertificateURL:

{
    "subject": {{ toJson .Subject }},
    "sans": {{ toJson .SANs }},
    "issuingCertificateURL": "http://crl.local/int.crt",
{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }}
    "keyUsage": ["keyEncipherment", "digitalSignature"],
{{- else }}
    "keyUsage": ["digitalSignature"],
{{- end }}
    "extKeyUsage": ["serverAuth", "clientAuth"]
}
maraino commented 2 years ago

Leaving it open to discuss if we should provide it by default. The problem with this is that step-ca doesn't know the exactly what needs. Which hostname from DNSNames? Which port if a proxy is used?

maraino commented 2 years ago

The new CRLs also have an extension indicating the URL of the CRL. Currently, this is set to the first DNSName without any port, it will default to 443 because it's HTTPS.

One option is to follow the same logic, another is to add a new field to be able to configure this.

foleyjohnm commented 2 years ago

doh! Maybe just need a documentation change to add the issuingCertificateURL. I did not see that anywhere, but could have totally missed it.

re: CRLs: Are you talking about the work of incorporating CRLs into Step? I was always taught that CRLs need to be retrieved over HTTP to possibly prevent a loop condition when a client pulls the CRL, and I believe that Microsoft products won't even download a CRL via HTTPS.

hslatman commented 2 years ago

Made the same mistake as @foleyjohnm; totally overlooked the fact that the AIA is in the x509.IssuingCertificateURL 😓

@foleyjohnm what @maraino means is that in our support for CRLs in the hosted CA, we automatically add the URL of the location of the CRL to the issued certificates. The URL is determined from the first DNS name that the CA listens on. We could do something similar for the AIA/IssuingCertificateURL, e.g. 1) automatically add the first CA DNS, 2) provide it as a configuration option or 3) leave it as is, so it can be provided in the template only.

It's a standard extension, so X509 implementations should be able to parse them and thus it should be relatively safe to add them by default. Maybe the configuration could be provided as an object, one property indicating it should be added or not, and the second property being a URL. If the URL is not provided, but the boolean is true, fall back to the default?

maraino commented 2 years ago

I've merged CRLs PR today, which is only for HTTPS, but we can add another simple PR to make them available when insecureAddress is used.

When CRLs are enabled, we can also add CRL Distribution Points by default. I've left this in the open-source triage project so that we can discuss the options in our next meeting.

Those extra extensions should not be added by default if a custom template is used.

foleyjohnm commented 2 years ago

A non-https interface would be good too. Keep in mind, CRLs are already signed, so putting them behind HTTPS is kind of like putting a lock on a lock.

I do think that CRLs should be configurable, and I would almost turn them off by default. I could see someone getting themselves into trouble if they were on by default, and you guys do a good job of explaining the perks/theory behind passive revocation. CRLs are something that needs serious consideration and planning.

One other suggestion- The CRL IDP should also match whatever the CRL location is

https://github.com/smallstep/certificates/blob/3e0b603eb44d85d83a257d4b691c71b80889849b/authority/tls.go#L742-750

For example, on my PKI, I like to host my CRLs on a load balanced set of servers. Ideally, I should be able to set the CDP of issued certificates to whatever URL I want, but the IDP of the CRL should match that.

I did build StepCA from source today and was messing around with the CRLs. I set the CDP to my own URL (manually saving the CRL to that location from the API), but windows was having a hard time verifying the CRL because the IDP on it was set to https://bla-bla-bla.local/1.0/crl. I don't know Go at all, but I did hardcode my URL into that fullName variable, and after rebuilding, all was right with the world with windows and the CRL.

maraino commented 2 years ago

I do think that CRLs should be configurable, and I would almost turn them off by default.

They are configurable and they are off by default.

One other suggestion- The CRL IDP should also match whatever the CRL location is

Yes, that was my point, we don't have a way to know for sure how the user access this, which port they use, if there's a proxy, load balancer, or which domain, ... Currently I'm assuming the user is using 443, but we can make this configurable, and we can set AIA, Distribution points, or any similar extension properly.

I did build StepCA from source today and was messing around with the CRLs. I set the CDP to my own URL (manually saving the CRL to that location from the API), but windows was having a hard time verifying the CRL because the IDP on it was set to https://bla-bla-bla.local/1.0/crl.

You should be able to configure the hostname and port to use for those extensions, but currently the path will be set to /1.0/crl, for example, assuming foo is the name of the configuration:

Currently, the logic uses the first domain in the dnsNames list.

We might need a configuration for the secure and insecure paths, we can default to the first domain in dnsNames and ports 443 and 80 if they are not configured.