rustls
rustls copied to clipboard
Improvements to client certificate support
Within webpki and prior to a client cert being accepted,
- [x] CRL support
- [x] Path validation, likely incorporating webpki CRL support
After a connection has been established,
- [ ] Provide pub api access to the [unparsed?] client certificate as well as issuer chain
- [ ] Provide pub api access to the parsed certificate entities/fields/extensions, including the Subject entity
The current status quo: rustls offers the ClientCertVerifier trait, which takes as input rustls::Certificate, which is a newtype around Vec<u8> - an unparsed, raw certificate. It is up to implementors of ClientCertVerifier to bring their own X.509 parser, path builder, and verifier. For instance, implementors might choose to use the webpki crate for those purposes.
rustls does offer two validating implementations of ClientCertVerifier that use webpki: AllowAnyAuthenticatedClient and AllowAnyAuthenticatedOrAnonymousClient. These say "if the client presents a certificate that chains to one of the configured roots, the client may connect."
For some uses that may suffice, but it's also common for servers to have more complex access requirements. For instance a server might want to say "the client must have a certificate that chains to one of the configured roots, and it must have SPIFFE ID xyz encoded in the Subject Alternative Names extension."
Or a server might want to say "the client may have any certificate or no certificate, but in the HTTP layer, if I'm handling a request for GET /admin, I will check the connection state. If the connection state contains a certificate with [email protected] in the Subject, I will return HTTP 200; otherwise I will return HTTP 403 to that request." (Note there are good arguments for why mixing TLS-level authentication with HTTP-level authorization is a bad idea - see Colm's arguments on the Security, Cryptography, Whatever podcast and this Twitter thread).
Also, one downstream user of rustls, via rustls-ffi, is Apache's mod_tls. Right now it doesn't support client certificates. One possible goal would be to support what's needed so mod_tls can reach feature parity with mod_ssl (which uses OpenSSL). Some of mod_ssl's client certificate related features:
Environment variables - https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#envvars
| Variable Name | Value Type | Description |
|---|---|---|
| SSL_CLIENT_M_VERSION | string | The version of the client certificate |
| SSL_CLIENT_M_SERIAL | string | The serial of the client certificate |
| SSL_CLIENT_S_DN | string | Subject DN in client's certificate |
| SSL_CLIENT_S_DN_x509 | string | Component of client's Subject DN |
| SSL_CLIENT_SAN_Email_n | string | Client certificate's subjectAltName extension entries of type rfc822Name |
| SSL_CLIENT_SAN_DNS_n | string | Client certificate's subjectAltName extension entries of type dNSName |
| SSL_CLIENT_SAN_OTHER_msUPN_n | string | Client certificate's subjectAltName extension entries of type otherName, Microsoft User Principal Name form (OID 1.3.6.1.4.1.311.20.2.3) |
| SSL_CLIENT_I_DN | string | Issuer DN of client's certificate |
| SSL_CLIENT_I_DN_x509 | string | Component of client's Issuer DN |
| SSL_CLIENT_V_START | string | Validity of client's certificate (start time) |
| SSL_CLIENT_V_END | string | Validity of client's certificate (end time) |
| SSL_CLIENT_V_REMAIN | string | Number of days until client's certificate expires |
| SSL_CLIENT_A_SIG | string | Algorithm used for the signature of client's certificate |
| SSL_CLIENT_A_KEY | string | Algorithm used for the public key of client's certificate |
| SSL_CLIENT_CERT | string | PEM-encoded client certificate |
| SSL_CLIENT_CERT_CHAIN_n | string | PEM-encoded certificates in client certificate chain |
| SSL_CLIENT_CERT_RFC4523_CEA | string | Serial number and issuer of the certificate. The format matches that of the CertificateExactAssertion in RFC4523 |
| SSL_CLIENT_VERIFY | string | NONE, SUCCESS, GENEROUS or FAILED:reason |
SSLCACertificateFile SSLCACertificatePath SSLCADNRequestFile SSLCADNRequestPath SSLCARevocationCheck SSLCARevocationFile SSLCARevocationPath SSLVerifyClient SSLVerifyDepth
Note: we almost certainly don't want to support all of these, but it's a useful reference for what Apache has implemented over the years.
CRLs / revocation
Is revocation more important for client certificates than for server certificates? Probably. One reason: client certificates are often used for authorization of individuals or machines, and authorization frequently has to be revoked. For instance if an administrator uses a client certificate to get access to a system, and they leave the organization or no longer have a trusted role, their certificate needs to be revoked.
Revocation and path building
Note there are two subtly different things: path building and path verifying. See sleevi's blog post for tons of details, and RFC 4518. For validating server certificates, there is a good argument that path building must take into account revocation status of intermediates. This allows clients to find alternate paths even if one of the paths they might find passes through a revoked certificate. This allows flexibility in the public Web PKI. Note that in practice, as described in sleevi's post, many implementations don't do this. For instance, the webpki crate doesn't yet take into account revocation status because it doesn't support revocation. OpenSSL does not take into account revocation status during path building because it does not do path building - it simply treats the chain from the TLS handshake as the only possible path.
For client certificates, do we need path building to take into account revocation status of intermediates? I think not. Usually the root certificates used for client certificates are simpler (more tree-like), and more locally controlled. For instance, a company might run an internal CA for issuing client certificates for their various services. These issuance hierarchies have less need to be flexible in the face of intermediate revocation, because they are fully controlled by a single entity. If an internal intermediate is revoked, the company can create a new intermediate and reissue and redeploy end entity certificates as needed.
@jsha Thanks for this writeup, it helps a lot with context and background!
Just wanted to mention I'm likely going to start looking at this problem space in the coming week. @jbr Happy to collaborate if you have thoughts or any work-in-progress on the go!
CRL support
I've started to sketch out the first piece for CRL support, parsing the DER representation: https://github.com/rustls/webpki/pull/44
CRL support in webpki landed w/ v0.101.0, the Rustls support for using it with the provided webpki-based client verifiers landed in v0.21.3. I'm going to leave this issue open because we haven't yet tackled the second half of the described improvements.
I think we can probably manage to expose the client certificate and associated unparsed attributes pretty easily in a future release. I believe my marching orders from my funding source are to switch gears to supporting the pluggable crypto backend work but I'd like to pursue this as well when time permits. It's something folks have been asking for separate from this issue (https://github.com/rustls/webpki/issues/28).
Exposing the issuer chain from EE -> trust anchor (e.g. https://github.com/briansmith/webpki/issues/68) is trickier. I've made a couple attempts but haven't been able to arrive at a good solution I could retrofit into the path building implementation that exists in webpki today. The biggest "catch" I've been bumping into is trying to avoid needing to return a reference to a stack variable (and with alloc a feature flag we want to avoid heap allocation too). For example we can't return a ref to the potential_issuer) that can live beyond the frame, but constructing it from outside the frame is tricky because it's a node in a linked list being constructed by the path building recursion. I haven't given up yet, but was nervous about making substantial changes to this thoughtfully written (and security load bearing) algorithm. I'm a bit less nervous on that front now that we're improving the path building test coverage (e.g. https://github.com/rustls/webpki/pull/116).