SSL Client Certificates - shawfdong/hyades GitHub Wiki

We use layered security for maia.ucsc.edu. Here we describe how we secure the site with SSL Client Certificates.

NOTE all major web browser vendors have made announcements about depreciating support for SHA1 in browsers and moving to SHA256. This guide is updated to use SHA256 (SHA2).

Table of Contents

Prepare a directory structure for the root CA

$ umask 0077
$ mkdir CA
$ cd CA
$ mkdir certs crl newcerts private
$ touch index.txt
$ echo 01 > serial
$ echo 01 > crlnumber

Create an OpenSSL configuration file for CA

The stock openssl.cnf is a good starting point. Here is my ca.cnf (placed in the CA directory created above):

[ ca ]
default_ca	= CA_default		# The default ca section

[ CA_default ]
dir		= .			# Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# 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/newcerts		# default place for new certs.

certificate	= $dir/dong-root-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/dong-root-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	= 1095			# how long to certify for
default_crl_days= 1095			# how long before next CRL
default_md	= sha256		# use public key default MD
preserve	= no			# keep passed DN ordering
policy		= policy_match

# For the CA policy
[ policy_match ]
countryName		= match
stateOrProvinceName	= match
organizationName	= optional
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional

####################################################################
[ req ]
default_bits		= 2048
default_keyfile 	= privkey.pem
distinguished_name	= req_distinguished_name
attributes		= req_attributes
x509_extensions		= v3_ca	# The extentions to add to the self signed cert
string_mask = utf8only

####################################################################
[ req_distinguished_name ]
countryName			= Country Name (2 letter code)
countryName_default		= US
countryName_min			= 2
countryName_max			= 2

stateOrProvinceName		= State or Province Name (full name)
stateOrProvinceName_default	= California

localityName			= Locality Name (eg, city)
localityName_default		= Santa Cruz

commonName			= Common Name (e.g. server FQDN or YOUR name)
commonName_max			= 64

emailAddress			= Email Address
emailAddress_max		= 64

####################################################################
[ req_attributes ]
challengePassword		= A challenge password
challengePassword_min		= 4
challengePassword_max		= 20

####################################################################
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
basicConstraints=CA:FALSE
nsComment			= "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

####################################################################
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

####################################################################
[ v3_ca ]
# Extensions for a typical CA
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true

####################################################################
[ crl_ext ]
# CRL extensions.
authorityKeyIdentifier=keyid:always

Generate the self-signed root certificate

Create a bash script (ca.sh) in the CA directory:

#!/bin/bash
### CA

umask 0077
base=dong-root-ca

openssl req \
  -x509 -new -days 3650 -newkey rsa:2048 -sha256 \
  -subj "/C=US/ST=California/L=Santa Cruz/CN=Frank Dong ROOT CA" \
  -keyout private/$base.key -out $base.crt -config ca.cnf

openssl x509 -in $base.crt -noout -text

then run:

$ ./ca.sh
which will produce 2 files: dong-root-ca.crt (the root certificate) and private/dong-root-ca.key (the private key).

Create a client certificate

Generate a CSR (Certificate Signing Request) using the following bash script (req.sh):

#!/bin/bash
### certificate request

umask 0077
if [ $# -ne 1 ]
then
  echo "Usage: $0 name"
  exit 1
fi

openssl req \
  -new -newkey rsa:2048 -sha256 -config ca.cnf \
  -keyout private/$1.key -out $1.csr

openssl req -in $1.csr -noout -text

For example:

$ ./req.sh dong01

Then sign the CSR using the following bash script (sign.sh)

#!/bin/bash
### to sign a certificate request

umask 0077
if [ $# -ne 1 ]
then
  echo "Usage: $0 name"
  exit 1
fi

openssl ca -config ca.cnf \
  -in $1.csr -out certs/$1.crt

For example:

$ ./sign.sh dong01

Export the certificate in PKCS12 format

For the client certificate to be used in a web browser, e.g., Firefox, it must to be exported to the PKCS #12 format. We use the following bash script (export.sh):

#!/bin/bash
### package the key and cert in a PKCS12 file

umask 0077
if [ $# -ne 1 ]
then
  echo "Usage: $0 name"
  exit 1
fi

openssl pkcs12 -export \
  -in certs/$1.crt -inkey private/$1.key -out $1.p12

For example:

$ ./export.sh dong01

It will produce dong01.p12 (the PKCS#12 certificate), which can then be imported by Firefox.

Generate a Certificate Revocation List

We use the following bash script (crl.sh) to generate a Certificate Revocation List:

#!/bin/bash
### create CRL

umask 0077
openssl ca -config ca.cnf -gencrl -out crl.pem

# binary format
openssl crl -inform PEM -in crl.pem -outform DER -out crl.der

openssl crl -in crl.pem -text

For example:

$ ./crl.sh
while will produce crl.pem (the CRL file). Although the Certificate Revocation List is empty at this stage, the file is still required by nginx.

Revoke a client certificate

In case a client certificate is stolen or compromised, we can revoke it with the following bash script (revoke.sh):

#!/bin/bash
## revoke a certificate ==

umask 0077
if [ $# -ne 1 ]
then
  echo "Usage: $0 name"
  exit 1
fi

openssl ca -config ca.cnf -revoke certs/$1.crt

### update the CRL
openssl ca -config ca.cnf -gencrl -out crl.pem

# binary format
openssl crl -inform PEM -in crl.pem -outform DER -out crl.der

openssl crl -in crl.pem -text

For example:

$ ./revoke.sh dong01

Configure nginx

Upload both crl.pem (the CRL file) and dong-root-ca.crt (the root certificate) to the directory /etc/ssl/ on the web server. Add the following lines to the configuration file of nginx (e.g., /etc/nginx/conf.d/maia.conf):

    ssl_client_certificate /etc/ssl/dong-root-ca.crt;
    ssl_crl /etc/ssl/crl.pem;
    ssl_verify_client on;

References

⚠️ **GitHub.com Fallback** ⚠️