4

I'm in a strange scenario where I have a server with NodeJS backend and ReactJS frontend that does record keeping where the customer wants to use user certificates to ID who visits this internal site. The problem is they have a very large network, with convoluted PKI, and the public cert I have been given to assign to the site doesn't necessarily match all the clients that could visit it.

I have nginx up front with the ssl_verify_client optional_no_ca; set but I noticed that browsers will only give users the option of selecting their client certificate if they have a certificate that is properly signed by the same CA as the public key presented by nginx.

My understanding is that during the certificate request the server can specify what CAs are acceptable. It seems like nginx may be doing this but I’m not sure that’s the case. My plan is start dissecting with wireshark tomorrow. Is there a known way to get my site to ask browsers to always prompt users for a client certificate? Have I perhaps misunderstood something along the way?

Grant Curell
  • 1,188

2 Answers2

5

Simple: Put into your nginx configuration what authorities certificates you want nginx to request. Nginx will send & verify those depending on your ssl_verify_client setting. Thats it.

ssl_client_certificate = /path/to/concatenated-x509.pem
# contents of /path/to/concatenated-x509.pem may contains, possibly many:
# -----BEGIN CERTIFICATE---- much foo bar -----END CERTIFICATE-----

The two directions of authenticating via certificates are unrelated. Your server does present a certificate, and there is no rule that says there cannot be any overlap, but otherwise, you may ask for whatever authority you want. Nginx does not even send the public key of the certificates your configure to be "sent" using the ssl_client_certificate. It sends a list of acceptable signature algorithms, and a list of the distinguished name sets of those certificates: something like /C=DK/O=ACME Inc./CN=ACME CA3.

EDIT: note other answer - NOT requesting any CA in particular is an option, too.

You may run into interoperability problems if you have to support older (pre-TLSv1.3) clients together with mixed signature algorithms (mix between EC and RSA), and if your list of distinguished names becomes excessively large. Try what works, specifically using the software that you need to support, and see to it that error messages are either self-explanatory or properly documented for whatever customers or business partners might encounter them.


‡) Thats it for getting a fully TLSv1.3-supporting client to present a certificate signed by any CA on your list. You are not done, then. Please have very little trust in assuming that everything signed by any of not-managed-by-you CAs is the authorized user the certificate suggests it is, and ensure very strict validation in your application behind the nginx proxy that actually deal with those optionally presented, possibly worthless/irrelevant certificates.

anx
  • 10,888
2

I want to sincerely thank @anx for his answer as it actually got me moving down the right path however it appears to be incorrect.

anx pointed me correctly to the docs

The docs say this about ssl_client_certificate:

Specifies a file with trusted CA certificates in the PEM format used to verify client certificates and OCSP responses if ssl_stapling is enabled. The list of certificates will be sent to clients. If this is not desired, the ssl_trusted_certificate directive can be used.

The reason we aren't getting the prompt is that nginx is sending a list of the CA certs to the browser and the browser looks at that. If the browser sees it doesn't have a client cert (with private key) matching that CA, it will not show the prompt.

As the docs say ssl_trusted_certificate however, does not send the list.

Specifies a file with trusted CA certificates in the PEM format used to verify client certificates and OCSP responses if ssl_stapling is enabled. In contrast to the certificate set by ssl_client_certificate, the list of these certificates will not be sent to clients.

I just finished testing it both ways; ssl_client_certificate causes browsers lacking matching certificates to not prompt whereas ssl_trusted_certificate does exactly what we want; it does not send a list of CA certs and always prompts.

On a usability note to anyone else who has to test this, you have to close and re-open the browser to get the prompt to show. A page refresh is insufficient.

Grant Curell
  • 1,188