Using Yubikey/PKCS11 for Upstream Client Certificates

erik nginx-forum at
Thu Feb 6 23:46:39 UTC 2020

I figured it out and thought I'd post back for anyone else looking at this
post in the future.

My problem had nothing to do with the PKCS#11 engine. It persisted when I
pointed proxy_ssl_certificate_key directly at the non-encrypted,
password-less rsa key file.

Instead, the problem was SNI. By default, Nginx uses the inbound request's
Host header as the upstream SNI name. Since I was hitting Nginx with curl on
localhost, it ended up sending "localhost" as the upstream virtual host.
It's even in the debug error log:

2020/02/05 07:40:28 [error] 25199#25199: *1 peer closed connection in SSL
handshake (104: Connection reset by peer) while SSL handshaking to upstream,
client: ::1, server: _, request: "GET /upstream HTTP/1.1", upstream:
"", host: "localhost" 

Since the upstream server does not have localhost as its SNI name, the TLS
connection failed to get established. By fixing the value for SNI it went

        proxy_ssl_server_name on;

I had to do a similar thing for the upstream HTTP Host header, which was
also being set to the value of the incoming request (again, localhost for

        proxy_set_header Host;

Now to get the full PKCS#11 uri for the Yubikey I ran:

  $ p11tool --provider /usr/lib/x86_64-linux-gnu/
--list-privkeys  --login
    Token 'PIV Card Holder pin (PIV_II)' with URL
requires user PIN
  Enter PIN:
  Object 0:
    Type: Private key
    Label: PIV AUTH key
    ID: 01

Prepending that with "engine:pkcs11:" and plugging that into

    proxy_ssl_certificate /etc/nginx/ssl/cert.pem;

And that made the whole thing work. Note that the client certificate itself
is still read from a file as proxy_ssl_certificate does not support pkcs11

I can now access the remote TLS server through the local proxy:

    $ curl http://localhost/foo/bar

Erik van Zijst

Posted at Nginx Forum:,286922,286966#msg-286966

More information about the nginx mailing list