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.78k stars 442 forks source link

Maintaining subject in certificate generated from CSR #1184

Open 61131 opened 2 years ago

61131 commented 2 years ago

Hello!

Issue details

Following on from #1181, it appears that step only persists the CommonName attribute when generating certificates from CSRs. Indeed, the signing process fails if the token includes other attributes within the subject. This can be demonstrated in the following example:

$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr -subj "/O=My Organisation/ST=California/C=US/CN=MyService/" -addext "subjectAltName = URI:urn:example.com:MyService" -addext "keyUsage = digitalSignature,keyEncipherment,dataEncipherment"
$ openssl req -in server.csr -text -noout
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: O = My Organisation, ST = California, C = US, CN = MyService
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    < ... hexadecimal bytes ... >
                Exponent: 65537 (0x10001)
        Attributes:
            Requested Extensions:
                X509v3 Subject Alternative Name:
                    URI:urn:example.com:MyService
                X509v3 Key Usage:
                    Digital Signature, Key Encipherment, Data Encipherment
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        < ... hexadecimal bytes ... >
$ TOKEN=$(step ca token "/O=My Organisation/ST=California/C=US/CN=MyService/" --san urn:example.com:MyService)
$ step ca sign --token $TOKEN ./server.csr server.pem
Provisioner: admin (JWK) [kid: iWxzVsCYtWVt7D0UdXLNnTuMRKRKDMCFU02PMb0Fr_o]
token subject '/O=My Organisation/ST=California/C=US/CN=MyService/' and CSR CommonName 'MyService' do not match

If however the token subject is restricted to the CommonName only, this process works, but the generated certificate will only include the CommonName attribute, despite the CSR including the full set of attributes:

$ TOKEN=$(step ca token "MyService" --san urn:example.com:MyService)
$ step ca sign --token $TOKEN ./server.csr server.pem
Provisioner: admin (JWK) [kid: iWxzVsCYtWVt7D0UdXLNnTuMRKRKDMCFU02PMb0Fr_o]
CA: https://localhost:9000
Certificate: server.pem
$ openssl x509 -in server.pem -inform pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            f3:53:e3:2e:1d:ae:83:55:77:3d:5f:6e:44:77:2b:ea
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: O = Smallstep, CN = Smallstep Intermediate CA
        Validity
            Not Before: Nov 14 22:55:06 2022 GMT
            Not After : Nov 15 22:56:06 2022 GMT
        Subject: CN = MyService
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    < ... hexadecimal bytes ... >
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Key Identifier:
                6C:8E:14:9F:DA:A3:1A:85:19:CC:8F:14:23:2D:9D:E1:B7:B8:59:6D
            X509v3 Authority Key Identifier:
                6C:14:B1:95:C1:AC:47:B9:6A:72:71:06:F3:E4:43:C3:C2:06:B6:C6
            X509v3 Subject Alternative Name:
                URI:urn:example.com:MyService
            1.3.6.1.4.1.37476.9000.64.1:
                07.....admin.+iWxzVsCYtWVt7D0UdXLNnTuMRKRKDMCFU02PMb0Fr_o
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        < ... hexadecimal bytes ... >

While it is known that more specialised certificate definitions can be generated using templates, this lacks the flexibility which honoring the subject field as provided in the CSR would provide.

Why is this needed?

This enhancement to honor the subject field as provided in the CSR when generating certificates would provide more immediate utility to Smallstep CA, requiring less "out of the box" configuration for a wider set of use-cases.

dopey commented 2 years ago

Hey @61131 👋, thanks for opening the issue!

First off - great question. We had some time to discuss this morning and I'll try to summarize our thoughts below.

The fact that that a client of step-ca cannot simply request a certificate where it can set the Organization, Country, State, etc. at will is by design. We feel that allowing a client to set these values would be too permissive. step-ca does not have any verification / validation of the other ASN1DN fields, so if any client could claim to be part of any organization (for example) we believe that could pose a security risk.

Our preference is that those fields be set by an administrator. This can be done a few different ways --

  1. At the authority level in the ca.json -- https://github.com/smallstep/certificates/blob/8a2e49a1e365fb60136bbf29145393efe36ef6b5/authority/config/config.go#L136-L165.
  2. By setting a template for a given provisioner.
61131 commented 2 years ago

Hey @dopey, thanks for the response.

We feel that allowing a client to set these values would be too permissive. step-ca does not have any verification / validation of the other ASN1DN fields, so if any client could claim to be part of any organization (for example) we believe that could pose a security risk.

This makes sense, but isn't the question of security addressed through the return of an appropriately scoped authentication token to authorise the signing action? Perhaps if controls are implemented around the permissible scope of token issue, greater flexibility can then be afforded in certificate generation. In any event, the proposition of using templates for certificate generation is noted and understood.

Thanks again.

dopey commented 2 years ago

This makes sense, but isn't the question of security addressed through the return of an appropriately scoped authentication token to authorise the signing action?

In a perfect world, yes. But that's not how our own tokens work. But our JWK and K8sSA provisioners don't do any validation on the "subject name" that the client requests. You've witnessed that, because you're able to generate a token with C=...,O=.... In order for us to do any more intelligent "authorization" before issuing a token an admin would need to configure policy rules somewhere (which is akin to having to configure templates or the ca.json). And that's just for the tokens that we issue. Other issuers (e.g., OIDC providers, IID, etc.) may not have the necessary configuration to properly authorize those fields.

I think another part of our inflexibility here is consideration for how we believe an Online CA should operate. The OpenSSL commands you've run in the example are for an "Offline CA". There is no authentication and the user running the commands is assumed to have admin privileges. Using OpenSSL you can sign over any CSR while Online CA's will only pull out a few fields (generally just the Common Name) from the CSR and then populate the rest of the certificate with default or preconfigured fields. The idea being to significantly limit the trust of user input.

That said, I agree with you in spirit. If it's in the token, and the CA trusts the entity that signed the token, then why doesn't the CA trust all the fields in the token?

I'll leave this open and if there's support from the community then we'll revisit with the team.