Table of Contents
X.509 client certificate authentication with mod_ssl on Apache2.2
apache openssl crypto ssl debian
Authenticating users is a pretty straightforward task. On todays internet you almost never see a web application that doesn't require a username and password to access specific features. But this mechanism has a trade off: you need to allow the visitor to negociate TCP and HTTP session before you process the credentials, thus opening a door for all sorts of content injections.
On some very specific web application, you might want to cover this risk. And the solution for this is to force the user to send a X.509 certificate to authentificate himself BEFORE you even access the data.
This X.509 certificate, when used on the client side, is generally called a client certificate. To successfully negociate a TLS session with the server, the server can require the client to furnish a valid client certificate. The server does so by sender a Certificate Request during the TLS Handhshake (right avec the ServerKeyExchange message, section 7.4.4 of the RFC).
So, what we will do in this article, is to configure the client certificate authentication in Apache 2.2 and explain how you can check the X509 fields to describe who can access what.
Generate a client certificate
Obviously, if you want to give x.509 certificates to your users, you need a PKI somewhere. I am not going to describe how to set up a whole PKI (I have already did it), but only describe the client certificate part.
For client certificate generation, I use a bash script similar to this one. The comments are directly in the script so it's easier to read. The script will generate a private key and a CSR, submit the CSR for signature to the Root CA private key, and bundle the whole thing into a PKCS#12 format (contains the private key and the certificate into a ciphered enveloppe).
#! /bin/sh if [ $# != 1 ] then echo "usage: sh genclient.sh <name or fqdn>" else mkdir $1 # generate a new pair of RSA keys # and a certificate request (CSR) openssl req -new -config ca/openssl.cnf \ -nodes -keyout $1/$1.key -out $1/$1.csr # Ask the RootCA to sign the CSR and # thus create a signed certificate # The configuration file (openssl.cnf) must # contain a section called [client_cert] openssl ca -config ca/openssl.cnf -extensions client_cert \ -in $1/$1.csr -out $1/$1.pem # Export the client signed certificate and the # private RSA key into a PKCS#12 file openssl pkcs12 -export -in $1/$1.pem -inkey $1/$1.key \ -name "$1 Personal Certificate" -certfile \ ./ca/ca-linuxwall.crt -out $1/$1.p12 echo echo "Certificate generated for $1" echo echo fi
in your OpenSSL configuration, you need a special section for client certificates. The [client_cert] in openssl.cnf for our client certificate generation is the following:
[ client_cert ] # These extensions are added when 'ca' signs a request. authorityKeyIdentifier=keyid,issuer subjectKeyIdentifier=hash subjectAltName=email:move issuerAltName=issuer:copy basicConstraints=CA:FALSE nsCertType = client, email, objsign keyUsage = nonRepudiation, digitalSignature, keyEncipherment
keyUsage is important here. Some enlightment from the RFC:
The nonRepudiation bit is asserted when the subject public key is used to verify digital signatures used to provide a non- repudiation service which protects against the signing entity falsely denying some action, excluding certificate or CRL signing.
The digitalSignature bit is asserted when the subject public key is used with a digital signature mechanism to support security services other than non-repudiation (bit 1), certificate signing (bit 5), or revocation information signing (bit 6). Digital signature mechanisms are often used for entity authentication and data origin authentication with integrity.
The keyEncipherment bit is asserted when the subject public key is used for key transport. For example, when an RSA key is to be used for key management, then this bit shall asserted.
Import it into you browser
Now that we have a PKCS#12 file for our user, we need to import it into the software the client is going to use to negociate the TLS session. Most likely, if your server is apache, your client will be Firefox :)
Firefox will also require the certificate of the Root CA in DER format.
The steps to import you certificate into Firefox are the following.
1. Go to Tools > Options > Advanced > Encryption
2. Click on View Certificates > Authorities There, you need to Import the PKI Root CA signed certificate. Check the 3 lines to make sure it's valid for all cases.
3. Still in the View Certificates window, go to Your certificates and import the PKCS#12 file. Firefox should ask you the password to unlock the PKCS#12 enveloppe and extract the private key and the certificate.
That's all. :)
Apache's virtual host configuration
I assume you have a working Apache Installation. If not, maybe setting up client certificate authentication is not the first thing you should try to do :)
The SSL support for Apache is provided by mod_ssl which, in turns, uses OpenSSL library.
On the server side, you also need a X.509 signed certificate generated on your PKI. Once again, I have already described this here.
Mod_SSL configuration
Let's suppose you have a virtual host that receives the requests directed to https://secure.mydomain.duh If, like me, you use Debian, you will most likely have a virtual host configuration file in /etc/apache2/sites-available/ that is called secure.
Let's start with the global SSL options:
# activate SSL for the virtual host SSLEngine On # path to the server signed certificate SSLCertificateFile /etc/apache2/certs/secure.mydomain.duh.pem # path to the server key SSLCertificateKeyFile /etc/apache2/certs/secure.mydomain.duh.key # path to Root CA signed certificate SSLCACertificateFile /etc/apache2/certs/ca-mydomain.duh.crt # path to CA Certificate Revocation List SSLCARevocationFile /etc/apache2/certs/crl-mydomain.duh.crl # duration of the session cache SSLSessionCacheTimeout 3600
Select the appropriate cipher list
mod_ssl uses the syntax of openssl to define a list of acceptable ciphers. The client will send a list of the ciphers he knows in the CLIENT HELLO message, and the server will select the best one that is present in the client's list and its own. You can test you CipherList in the openssl command line to check which ciphers are going to be accepted.
For example, if you want to use the directive SSLCipherSuite ALL:!LOW:!MED:!SSLv2:!MD5:!DES, you can test the list of corresponding ciphers as follow:
$ openssl ciphers -v 'ALL:!LOW:!MED:!SSLv2:!MD5:!DES' ADH-AES256-SHA SSLv3 Kx=DH Au=None Enc=AES(256) Mac=SHA1 DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 ADH-AES128-SHA SSLv3 Kx=DH Au=None Enc=AES(128) Mac=SHA1 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 DHE-DSS-AES128-SHA SSLv3 Kx=DH Au=DSS Enc=AES(128) Mac=SHA1 AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1 ADH-DES-CBC3-SHA SSLv3 Kx=DH Au=None Enc=3DES(168) Mac=SHA1 EDH-RSA-DES-CBC3-SHA SSLv3 Kx=DH Au=RSA Enc=3DES(168) Mac=SHA1 EDH-DSS-DES-CBC3-SHA SSLv3 Kx=DH Au=DSS Enc=3DES(168) Mac=SHA1 DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1 RC4-SHA SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=SHA1
The columns correspond to the fields defined in the appendix C of the RFC
This list of ciphers is quite acceptable. We can add it to our virtual host file as follow:
# list of accepted ciphers SSLCipherSuite ALL:!LOW:!MED:!SSLv2:!MD5:!DES
So far, this is a classic Server side configuration. Nothing specific to client certificate authentication here.
It's coming now :)
Give access to specific resources
Imagine that you want to protect the folder containing the photos of your drunk brother humping a pumpkin (and yes you might want to do that), you need to add the following section to your virtual host file:
<Directory /var/www/secure/drunkbrotherhumpkinpumkin/> Options Indexes FollowSymLinks AllowOverride None Order allow,deny allow from all SSLVerifyClient Require SSLVerifyDepth 3 SSLRequire %{SSL_CLIENT_S_DN_O} eq "Family" </Directory>
The first part of the Directory is classic, but the second is more interesting. Basically, it says the following:
- SSLVerifyClient Require imposes the use of a client certificate to establish a TLS session with the server
- SSLVerifyDepth 3 indicates the maximum level of verification of the client certificate with the Root CA. We give the possibility to have a intermediate CA between the Root CA and the client.
- SSLRequire %{SSL_CLIENT_S_DN_O} eq “Family” checks that the X.509 certificate has an organization field set to Family. You can thus control who is accessing this folder among the users of the PKI.
Similarly, you can control the access to a proxy directive as follow:
<Proxy *> Order deny,allow Allow from all SSLVerifyClient Require SSLVerifyDepth 3 SSLRequire %{SSL_CLIENT_S_DN_O} eq "Administrator" </Proxy> ProxyRequests Off ProxyPass /control-panel/ http://localhost:10000/ ProxyPassReverse /control-panel/ http://localhost:10000/
Yes, this is a proxy to a webmin interface. Yes webmin is a major security flaw on a production server, but if you must have one, at least have it secured ;)
Some mod_ssl tuning
Entropy source
You might want to specify which entropy source is used by mod_ssl. On Linux, the best entropy source available is /dev/random, but /dev/urandom is not bad either.
What's the difference ?
When read, the /dev/random device will only return random bytes within the estimated number of bits of noise in the entropy pool. /dev/random should be suitable for uses that need very high quality randomness such as one-time pad or key generation. When the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered. A read from the /dev/urandom device will not block waiting for more entropy. As a result, if there is not sufficient entropy in the entropy pool, the returned values are theoretically vulnerable to a cryptographic attack on the algorithms used by the driver. Knowledge of how to do this is not available in the current non-classified literature, but it is theoretically possible that such an attack may exist. If this is a concern in your application, use /dev/random instead.
Note that the entropy pool is only 4KB… so if you use /dev/random and SSL is slow, take a look at :
# cat /proc/sys/kernel/random/entropy_avail 3586
Anyway, if you want to set this in Apache, add the following line somewhere in httpd.conf:
SSLRandomSeed startup file:/dev/random 1024 SSLRandomSeed connect file:/dev/random 1024
SSL Session Cache
If you want to enable session caching, and avoid complete handshakes every time a client reconnect, add the following to httpd.conf:
SSLSessionCache dbm:/var/cache/apache2/sslcache
~~DISCUSSION~~