Nginx – client SSL certificate verify error: (27:certificate not trusted)

certificate-authoritynginxsslssl-certificatetls

I'm having some difficulty with nginx's client authentication while using an intermediate CA (self-created).

Although the same certificate bundle (intermediate + root certificates in a single .pem file) works just fine for client authentication in IMAP (dovecot) and SMTP (postfix), I just can't seem to get it to work with nginx. Instead I'm getting the following error:

[info] 23383#23383: *14583139 client SSL certificate verify error: (27:certificate not trusted) while reading client request headers, client: 82.39.81.156, server: <hostname>, request: "GET /mailboxes HTTP/1.1", host: "<hostname>"

As I understand it, an error type 27 from openssl is X509_V_ERR_CERT_UNTRUSTED, or some kind of issue with a certificate being untrusted for a particular purpose, however I can't get it to elaborate any further.

The individual and bundled certificates all seem to validate correctly with openssl verify (I can verify client certificates against intermediate or the bundle, and the intermediate certificate validates against the root certificate, i.e- it's all valid in every combination I can think of).

My root and intermediate CAs should be set with the correct extensions in order to verify my client certificates, here's the relevant sample of my custom openssl.conf file:

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
basicConstraints        = critical, CA:true
keyUsage                = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
basicConstraints        = critical, CA:true, pathlen:0
keyUsage                = critical, digitalSignature, cRLSign, keyCertSign

Meanwhile the client certificates that I'm issuing are configured to work for both client authentication and e-mail encryption/signing like so:

[ user_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints        = CA:FALSE
nsCertType              = client, email
nsComment               = "OpenSSL Generated Client Certificate"
subjectAltName          = email:move
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer
keyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage        = critical, clientAuth, emailProtection

(indeed, if they weren't set correctly then OS X's Safari and Mail wouldn't let me send them at all, as they tend to be really strict).

A number of similar questions have recommended setting ssl_verify_depth to a value of 2 (to verify both the intermediate and root certificates), but this doesn't seem to help.

Best Answer

I have the same setup, and have been trying to diagnose this exact issue. The certs worked just fine on an apache instance, but nginx was being a problem. This is the solution I've come up with.

Point your ssl_client_certificate at your root certificate. Not your intermediate. Then also ensure that nginx verifies to a depth of 2. I've tried having a certificate chain file as the paramater for the client certificate, and it still didn't work. I don't understand why it has to be the root, instead of the intermediate that signed the cert. But this works.

For completeness, here's what the relevant parts of my nginx configuration look like:

server {
    listen 8443 ssl;
    server_name               www.example.com;

    ssl                       on;
    ssl_certificate           /path/to/cert.pem;
    ssl_certificate_key       /path/to/key.key;
    ssl_client_certificate    /path/to/root.ca.cert;
    ssl_verify_client         on;
    ssl_verify_depth          2;
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers               <snipped-for-length>;
}