Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verification of client cert checks issuer chain past trusted CA #29630

Closed
risacher opened this issue Sep 20, 2019 · 7 comments
Closed

Verification of client cert checks issuer chain past trusted CA #29630

risacher opened this issue Sep 20, 2019 · 7 comments
Labels
feature request Issues that request new features to be added to Node.js. stale tls Issues and PRs related to the tls subsystem.

Comments

@risacher
Copy link

  • Version: v12.10.0
  • Platform: Linux birch 4.15.0-1044-aws Tracking / Assuring Compatibility #46-Ubuntu SMP Thu Jul 4 13:38:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  • Subsystem: tls or crypto

When calling socket.renegotiate({requestCert: true, rejectUnauthorized: true}, cbFunc), the client certificate is verified against the trusted certs passed to the ca option of https.createServer(). This process fails if the entire chain cannot be verified to a self-signed certificate (i.e. a root cert), with socket.authorizationError set to UNABLE_TO_GET_ISSUER_CERT. I suspect this is a problem even without trying to use renegotiation.

Here's why this is a problem: my employer has about 3 million users with smartcards, each of which has 3-4 user certificates - one for email encryption, one for identity proofing, one for digital signatures, etc. These certificates are issued by different intermediate CAs, but the intermediate CA certificates are issued by a single root CA.

When the https.server requests a client cert, it passes the DN of all trusted CAs to the client. If the root CA is included, then the browser will prompt the user to select which certificate to send, which will be all available certs on the smartcard (ID, email, and signature), and the user will often pick the "wrong" cert.

Alternatively, if only the desired intermediate CAs are trusted (the ID CA, say), then the user will only be presented the single user-certificate that was issued by that CA, but verification will fail, presumably because the trusted intermediate CA cert cannot be verified up to a self-signed root.

I think this is actually a limitation in SSL_get_verify_result() - that it only accepts self-signed certs as trust anchors - which doesn't align with RFC 5280, which says "The selection of a trust anchor is a matter of policy: it could be the top CA in a hierarchical PKI, the CA that issued the verifier's own certificate(s), or any other CA in a network PKI."

Alternatively, is there a way to limit the list of allowable CAs presented in the requestClientCert message to a subset of the trusted CAs?

@bnoordhuis bnoordhuis added the tls Issues and PRs related to the tls subsystem. label Sep 23, 2019
@bnoordhuis
Copy link
Member

I think I understand your question but I believe the answer in all cases is "sorry, you can't do that."


Certificates are checked starting from the root down to the leaf cert.

In principle node can track through its SSL_CTX_set_cert_verify_callback() callback whether a rejected cert comes before or after the CA that's passed to tls.Server() or https.Server() but you don't know if the chain is valid when there's no trusted root.


There is a way to restrict the list of CAs that are sent in the handshake (SSL_set_client_CA_list()) but I believe openssl only implements that for TLSv1.3 so that probably doesn't help you much.

I'm not 100% sure about it being TLSv1.3-only but I can't seem to make it work with TLSv1.2 and there's at least one place in openssl's code base where the CA list is deliberately restricted to a TLSv1.3 handshake.


You can disable validation if you really want to (and are really careful) and do it yourself. tls.TLSSocket#getPeerCertificate() is the method you're looking for.

@risacher
Copy link
Author

As a workaround, I'm implementing precisely what you suggested: setting rejectUnauthorized: false, and validating in the app with fidm/x509

But what you point out means that (maybe) I was wrong in that the issue is not with OpenSSL. Rather, the limitation is that from node there's no way to call SSL_set_client_CA_list, other than by calling SSLWrap::SetCACerts() which both calls both SSL_set1_verify_cert_store and SSL_set_client_CA_list on the same input.

The docs for SSL_set_client_CA_list() in OpenSSL 1.1.1 discuss both TLS 1.2 and 1.3 and the differences, and my read is that it is not TLSv1.3-only for server-to-client. Apparently, in TLSv1.3 the client can also specify a list of acceptable CAs, to the server, but that seems to be mostly pointless.

Could another option be added to renegotiate() for the client_CA_list?

@bnoordhuis
Copy link
Member

Sure, pull requests are welcome.

@bnoordhuis bnoordhuis added the feature request Issues that request new features to be added to Node.js. label Sep 24, 2019
@pkgulati
Copy link

pkgulati commented Jun 30, 2021

Will it be better to use maximum depth option for verifying certificate, so that by using appropriate hierarchy, a particular set of certificates at lower depth will become valid for verify purpose, even if full chain is supplied. Open SSL allows SSL_CTX_set_verify_depth method to set the maximum depth. #39208

Open SSL Verification

Verification happens till it has reached a self-signed cert or num > max_depth or can't find an issuer in the untrusted list.
After that it stops looking there and start looking only in the trust store if enabled.

@risacher
Copy link
Author

@pkgulati I think your suggestion would work for my use-case, even if it's a little inelegant (IMHO). If #39208 were implemented, I think it would fix this too.

@github-actions
Copy link
Contributor

There has been no activity on this feature request for 5 months and it is unlikely to be implemented. It will be closed 6 months after the last non-automated comment.

For more information on how the project manages feature requests, please consult the feature request management document.

@github-actions github-actions bot added the stale label Mar 29, 2022
@targos targos moved this from Pending Triage to Stale in Node.js feature requests Mar 29, 2022
@targos targos moved this to Pending Triage in Node.js feature requests Mar 29, 2022
@github-actions
Copy link
Contributor

There has been no activity on this feature request and it is being closed. If you feel closing this issue is not the right thing to do, please leave a comment.

For more information on how the project manages feature requests, please consult the feature request management document.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js. stale tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants