Opened 7 years ago

Closed 7 years ago

#301 closed defect (invalid)

SSL: client certificate verification not working with intermediate certificates

Reported by: Jacek L Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.2.x
Keywords: ssl client certificate intermediate Cc:
uname -a: Linux asdf 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.2.7
TLS SNI support enabled
configure arguments: --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/ --with-pcre-jit --with-debug --with-file-aio --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_secure_link_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-auth-pam --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-dav-ext-module --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-echo --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-upstream-fair --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-syslog --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/nginx-cache-purge --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/ngx_http_pinba_module --add-module=/usr/src/nginx/source/nginx-1.2.7/debian/modules/ngx_http_substitutions_filter_module


Nginx in during verification client certificates doesn't support correctly intermediate certificates. My certificates self created: (RootCA is selfsigned, IntrermediateCA1/2 are signed by RootCA, etc.)

 RootCA -> IntermediateCA1 -> Client1 
 RootCA -> IntermediateCA2 -> Client2

I want to use in nginx "IntermediateCA1", to allow access to site only to owner of the "Client1" certificate. Part of vhost configuration:

server {
listen 443 ssl;
ssl_client_certificate /path/to/IntermediateCA1.crt; #changed to IntermediateCA1+RootCA, etc.
ssl_verify_client on;
ssl_verify_depth 2; #changed to 1,2,3..

When I put to "ssl_client_certificate" file with IntermediateCA1 and RootCA, and set "ssl_verify_depth 2" (or more) , clients can login to site both using certificate Client1 and Client2 (should only Client1).
The same result is when I put to "ssl_client_certificate" file with only RootCA - both clients can login.

When I put to "ssl_client_certificate" file with only IntermediateCA1, and set "ssl_verify_depth 1" (or "2" or more - no matter) , it is imposible to log in, I get error 400. And in debug mode i see logs:

verify:0, error:20, depth:1, subject:"/C=PL/CN=IntermediateCA1/",issuer: "/C=PL/CN=RootCA/"
verify:0, error:27, depth:1, subject:"/C=PL/CN=IntermediateCA1/",issuer: "/C=PL/CN=RootCA/"
verify:1, error:27, depth:0, subject:"/C=PL/CN=Client1/",issuer: "/C=PL/CN=IntermediateCA1/"
client SSL certificate verify error: (27:certificate not trusted) while reading client request headers, (..)

I thing this is a bug. Tested on Ubuntu, nginx 1.1.19 and 1.2.7-1~dotdeb.1, openssl 1.0.1 and/or 9.0.8. Tested in client certificate with and without certificate chain (using browser: Chrome).
I see that nginx 1.3 has few more options about using client certificates (eg."optional_no_ca"), but I don't see there solution to this problem.

Currently, the only one way to separate clients 1 and 2 is to create two, selfsigned RootCAs, but this is only workaround. There should by possibilites to use any intermediate cerfiticate to verify clients with certyficates signed by this intermediate certificate.

The problem is confirmed by several people:

Change History (1)

comment:1 by Maxim Dounin, 7 years ago

Resolution: invalid
Status: newclosed

This is how certificate verification works: certificate must be verified up to a trusted root. If chain can't be built to a trusted root (not intermediate) - verification fails. If you trust root - all certificates signed by it, directly or indirectly, will be successfully verified.

Limiting verification depth may be used if you want to limit client certificates to a directly issued certificates only, but it's more about DoS prevention, and obviously it can't be used to limit verificate to intermediate1 only (but not intermediate2).

What you want here is some _authorization_ layer based on the verification result - i.e. you may want to check that client's certificate issuer is intermediate1. Simplest solution would be to reject requests if issuer's DN doesn't match one allowed, e.g. something like this (completely untested):

    server {
        listen 443 ssl;

        ssl_certificate ...
        ssl_certificate_key ...

        ssl_client_certificate /path/to/ca.crt;
        ssl_verify_client on;
        ssl_verify_depth 2;

        if ($ssl_client_i_dn != "CN=Intermediate CA1") {
            return 403;
Note: See TracTickets for help on using tickets.