Nginx and Client Certs

How to setup nginx requiring client certificates for authentication.

This is based on the articles from arcwebtech and Victor Barzin.

Assumption: everything is stored at /usr/local/etc/ca

Creating a Certificate Authority (CA)

config file for the ca

This is just an example and probably not a good one. Save this at /usr/local/etc/ca/ca.conf:

[ ca ]
default_ca = CA_default                 # The name of the CA configuration to be used.
                                        # can be anything that makes sense to you.
[ CA_default ]
dir = /usr/local/etc/ca                       # Directory where everything is kept
certs = $dir/certs                      # Directory where the issued certs are kept
crl_dir = $dir/crl                      # Directory where the issued crl are kept
database = $dir/index.txt               # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several certificates with same subject.
new_certs_dir = $dir/certs              # Default directory for new certs.
certificate = $dir/ca.crt               # The CA certificate
serial = $dir/serial                    # The current serial number
crlnumber = $dir/crlnumber              # The current crl number
                                        # must be commented out to leave a V1 CRL
crl = $dir/crl.pem                      # The current CRL
private_key = $dir/private/ca.key       # The private key
RANDFILE    = $dir/private/.rand        # private random number file
x509_extensions = usr_cert              # The extentions to add to the cert
name_opt = ca_default                   # Subject Name options
cert_opt = ca_default                   # Certificate field options
default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md    = sha1                    # use public key default MD
preserve    = no                        # keep passed DN ordering
policy = policy_match

[req]
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = US
ST = IL
L = City

setup the directories & files

mkdir /usr/local/etc/ca/certs
mkdir /usr/local/etc/ca/certs/users
mkdir /usr/local/etc/ca/crl
mkdir /usr/local/etc/ca/private

touch /etc/ssl/ca/index.txt
echo ’01’ > /etc/ssl/ca/crlnumber

generate key, cert and revocation list for the CA

openssl genrsa -des3 -out /usr/local/etc/ca/private/ca.key 4096

openssl req -config /usr/local/etc/ca/ca.conf \
    -new -x509 -days 1095 \
    -key /usr/local/etc/ca/private/ca.key \
    -out /usr/local/etc/ca/certs/ca.crt

openssl ca -config /usr/local/etc/ca/ca.conf \
    -name CA_default -gencrl \
    -keyfile /usr/local/etc/ca/private/ca.key \
    -cert /usr/local/etc/ca/certs/ca.crt \
    -out /usr/local/etc/ca/private/ca.crl \
    -crldays 1095

Create Certs for Users

Please Note:

  • you have to fill out the common name, else the cert cannot be imported
  • check which password you have to fill in
  • set a password for export, don't leave blank - MacOS is picky about this.
openssl genrsa -des3 -out /usr/local/etc/ca/certs/users/<USERNAME>.key 1024

openssl req -new \
    -key /usr/local/etc/ca/certs/users/<USERNAME>.key \
    -out /usr/local/etc/ca/certs/users/<USERNAME>.csr

openssl x509 -req \
    -days 1095 \
    -in /usr/local/etc/ca/certs/users/<USERNAME>.csr \
    -CA /usr/local/etc/ca/certs/ca.crt \
    -CAkey /usr/local/etc/ca/private/ca.key \
    -CAserial /usr/local/etc/ca/serial \
    -CAcreateserial \
    -out /usr/local/etc/ca/certs/users/<USERNAME>.crt

openssl pkcs12 \
    -export \
    -certfile /usr/local/etc/ca/certs/ca.crt \
    -in /usr/local/etc/ca/certs/users/<USERNAME>.crt \
    -inkey /usr/local/etc/ca/certs/users/<USERNAME>.key \
    -out /usr/local/etc/ca/certs/users/<USERNAME>.p12

Transfer the created certificate /usr/local/etc/ca/certs/users/<USERNAME>.p12 to the client's machine and install it in their favorite browser(s).

Revoking as user cert

openssl ca  -config /usr/local/etc/ca/ca.conf \
    -revoke /usr/local/etc/ca/certs/users/<USERNAME>.crt \
    -keyfile /usr/local/etc/ca/private/ca.key \
    -cert /usr/local/etc/ca/certs/ca.crt

openssl ca  -config /usr/local/etc/ca/ca.conf \
    -gencrl \
    -keyfile /usr/local/etc/ca/private/ca.key \
    -cert /usr/local/etc/ca/certs/ca.crt \
    -out /usr/local/etc/ca/private/ca.crl \
    -crldays 1095

This will take effect after the defined session time (see nginx config) or immediately after reloading the nginx config.

Configuring Nginx

Do not remove the https cert definitions. These are still needed.

Add the following lines to the server block:

[...]
    ssl_client_certificate /etc/ssl/ca/certs/ca.crt;
    ssl_crl /etc/ssl/ca/private/ca.crl;
    ssl_verify_client on;
    ssl_session_timeout 5m;

There is a method to make client verification optional and then check in child blocks (like location) for verification success, e.g. described here.