Welcome! Log In Create A New Profile


Re: ssl client auth trouble

August 30, 2019 06:58PM
I'm a big fan of throw-away certificates, i.e., self-signed certificates you may dispose of any time. It seems, the generation of proper certificates is still a mystery to some, so let me briefly include a recipe how to create them:

Create a cert-client.conf of the following form:

# Client certificate request

[ req ]
default_bits = 4096 # RSA key size
encrypt_key = yes # Protect private key
default_md = sha256 # MD to use
utf8 = yes # Input is UTF-8
string_mask = utf8only # Emit UTF-8 strings
prompt = no # Prompt for DN
distinguished_name = email_dn # DN template
req_extensions = email_reqext # Desired extensions

[ email_dn ]
0.domainComponent = "com"
1.domainComponent = "example"
2.domainComponent = "project"
organizationName = "{{ORG}}"
organizationalUnitName = "{{CUSTOMER}}"
commonName = "{{NAME}}"
emailAddress = "{{EMAIL}}"

[ email_reqext ]
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = emailProtection,clientAuth
subjectKeyIdentifier = hash
subjectAltName = email:{{EMAIL}}

Replace project.example.com and the stuff in {{...}} accordingly. I keep the domain components usually fixed for a project, while the rest is dynamically replaced with sed.

Assume, you have an instance of cert-client.conf replaced properly, and it's in wherever the value of $conf points to. Assume $name is the name you want to give to this certificate. Then you create it with

openssl req -new -newkey rsa:4096 -x509 -days 365 -nodes -config $conf -sha256 \
-passout "pass:" -out "$name.pem" \
-keyout "$name.key"
openssl pkcs12 -export \
-inkey "$name.key" -in "$name.pem" \
-passin "pass:" \
-out "$name.pkcs12" \
-passout "pass:" \
-name "$name"
openssl x509 -text -noout -in "$name.pem" \
-passin "pass:" > "$name.info"

This creates a PEM file (public key), key file (private key), and pkcs12 file (certificate).

For Chrome and Firefox, use the respective configuration tool of the browser to introduce this as a new personal certificate. For IE, it is sufficient to import the certificate into the Windows certificate store.

For the server side of TLS, I usually use something of this sort:

# SAN certificates are defined once on the http context level
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/private.pem;
ssl_protocols TLSv1.2 TLSv1.3;

# TLSv1.3 requires openssl 1.1.1 or later. This tries to enable 0-RTT.
#ssl_early_data on;

# Prefer the faster Diffie Hellman Parameters over slower RSA algorithms
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
ssl_dhparam /etc/nginx/certs/dhparam.pem;

# SSL session parameters
ssl_session_cache shared:SSL:36m;
ssl_session_timeout 3m;
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/certs/ticket.key;
ssl_session_ticket_key /etc/nginx/certs/ticket.key.old;

with fullchain.pem being the public keys of the certificate chain, starting with the most specific, i.e., the server's SAN certificate. I use a little awk script to re-arrange public keys in the proper order - from the output of a "openssl pkcs7 -print_certs ..."

and with dhparam.pem generated by

openssl dhparam -out /etc/nginx/certs/dhparam.pem 4096

and ticket.key generated by

openssl rand 80 > /etc/nginx/ticket.key

Next thing is that TLS can only be switched on per server, not per location. I generally don't like SNI, so I prefer to have SAN certificates for servers.

server {
listen 443 ssl;
server_name ...{all the names you need}...;
ssl_trusted_certificate /etc/nginx/certs/clients.pem;
ssl_verify_client optional_no_ca;
ssl_verify_depth 1;


will do the trick, if clients.pem is simply a concatenation of all .pem files you have produced earlier (the public keys of clients who are supposed to have access). Note that I use the directive "ssl_trusted_certificate", so the list of permitted CAs is not communicated, but rather the client has to present a certificate. There is no requirement to use any specific CA. As each certificate is self-signed, the "CA" (issuer) of each certificate is the certificate itself.

You may now check in the nginx.conf or in a Javascript handler what the client status is:

$ssl_client_verify will be NONE if no certificate was presented, SUCCESS if the client specified one of the permitted certificates, and FAILED... if there was some failure.

Once this is done, you can check $ssl_client_s_dn to find out who was authenticated. You may use a map directive to map this variable to the e-mail address from the certificate's DN, or look at any one of the other attributes. What you can query you'll find here: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables

This should be a complete recipe on how to set up working self-signed client certificates.

If this works, you may play with other options :-)

Subject Author Posted

ssl client auth trouble

aweber August 30, 2019 10:10AM

RE: ssl client auth trouble

Reinis Rozitis August 30, 2019 12:34PM

Re: ssl client auth trouble

aweber August 30, 2019 03:14PM

RE: ssl client auth trouble

Reinis Rozitis August 30, 2019 03:22PM

Re: ssl client auth trouble

j94305 August 30, 2019 06:58PM

Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 72
Record Number of Users: 6 on February 13, 2018
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready