Secure communication

TLS (Transport Layer Security) and its predecessor SSL provide secure communication over a computer network. The most common use for TLS/SSL is for establishing an encrypted link between a web server and a browser. This allows you to guarantee that all data passed between the browser and the web server is private and not tampered with.

You can use certificates on both sides, the server side and the client side.

Server side verification

Web site certificates, or server side verification, allow a user to verify that the browser is connecting to the correct web site.

You can get web server certificates from different providers. Most SSL providers have extensive documentation on how to configure your web server with certificates. In this post I’ll mostly focus using on client side certificates.

Certificate Request

Basically what you have to do is generate a certificate request (a .CSR file) and send this to your certificate vendor. They will then send you a certificate file (a .CRT). You will also have to download the certificate chain file (also a .CRT) from your provider.

Apache SSL configuration

Once you get the certificate file you have to configure Apache. In the virtual host that you want to protect you need to enable SSL and point it to the certificate file, the private key file and the certificate chain file.

SSLEngine on SSLCertificateFile /etc/apache2/mycertif/mycertif.crt SSLCertificateKeyFile /etc/apache2/mycertif/mycertif.be.key SSLCertificateChainFile /etc/apache2/mycertif/myproviderCA.crt

Do not forget to restart Apache after you have changed the configuration.

Client side verification

Another interesting feature of certificates is that you can use them to authenticate users. Instead of having a database of usernames and passwords you provide your users a certificate. They will then need to import it in their browser and can use that certificate to authenticate themselves with your web site.

A certificate is not a bullet proof solution. If you are able to steal the certificate, or have access to the browser, then you can impersonate the certificate owner. Modern malware sometimes tries to steal certificates from the browser. If a certificate gets stolen then the administrator (certificate authority) has to revoke the certificate and issue a new one.

Certificates are issued by CAs, certificate authorities. This is both the case for server side and client side certificates. Because there are not a lot of certificate providers that let you generate client certificates I decided to generate them myself. This meant setting up my own CA.

Build your own CA

You can setup your own CA and issue certificates with openssl.

Because anyone having access to your certificate CA files will also be able to generate their own certificates impersonating your CA it is important to limit access to these files. First setup a separate directory /etc/apache2/myca/ that will contain the CA and configuration files. Make sure that this directory is not easily accessible (restrict access to root only).

mkdir /etc/apache2/myca chown root:root /etc/apache2/myca chmod 700 /etc/apache2/myca

Now we have to create the openssl.cnf configuration file. Do this in the directory /etc/apache2/myca.

[ req ] default_md = sha1 distinguished_name = req_distinguished_name [ req_distinguished_name ] countryName = Country localityName = Locality organizationName = Organization organizationalUnitName = Unit emailAddress = emailaddress commonName = Common Name [ certauth ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always basicConstraints = CA:true [ client ] basicConstraints = critical,CA:FALSE keyUsage = digitalSignature, keyEncipherment, dataEncipherment extendedKeyUsage = clientAuth

The next step is to generate the self signed certificate CA. It is valid for 3650 days and stored in ca.cer. We’ll use it to issue the client certificates.

openssl req -config ./openssl.cnf -newkey rsa:2048 -nodes -keyform PEM -keyout ca.key -x509 -days 3650 -extensions certauth -outform PEM -out ca.cer

You’ll then get a couple of questions to answer. It doesn’t really matter what you enter but because some of the information is returned when verifying a certificate it makes sense to provide something meaningful.

Generating a 2048 bit RSA private key ... Country []:BE Locality []:Brussels Organization []:MyOrg Unit []:MyDpt emailaddress []:ca@myorg.be Common Name []:MyOrg CA

This is all that is needed to setup your own CA. The CA certificate is stored in ca.cer, the private key in ca.key.

Client certificate

Now we take on the role of a user requesting a certificate. First step is to generate a private key

openssl genrsa -out client.key 2048

This generates a 2048 bit private key stored in the file client.key.

Now generate the certificate signing request. This will result in the .req file holding the request.

openssl req -config ./openssl.cnf -new -key client.key -out client.req

Similar to generate the certificate CA you have to provide some certificate information. Make sure that you uniquely specify a Common Name (the ‘real name’ of the certificate holder) and correctly set the email address, organization and optionally the unit. Remember that in this phase you are acting as the user requesting a certificate, you are not acting as the CA.

You are about to be asked to enter information that will be incorporated into your certificate request. ... Country []:BE Locality []:Brussels Organization []:MyOrg Unit []:MyDpt emailaddress []:koen.vanimpe@myorg.be Common Name []:Koen Van Impe

Now that you have created a certificate signing request you have to take the role of the CA again and issue a client certificate. The client certificate will be stored in the client.cer file.

openssl x509 -req -in client.req -CA ca.cer -CAkey ca.key -extfile openssl.cnf -extensions client -days 365 -outform PEM -out client.cer -CAcreateserial -CAserial serial.seq

Note that the command above takes care of generating unique serial numbers (CAcreateserial). The serials are stored in a file serial.seq (CAserial).

The last step is to convert this client certificate into something that can be used by the user. Users can import certificates in the browser in PKCS#12 format. This means we have to convert the .cer file into a .p12 file.

openssl pkcs12 -export -inkey client.key -in client.cer -out client.p12

You’ll be prompted to enter a password. This password is needed to “unlock” the certificate to make sure that not everyone who is able to intercept the certificate during transport is able to use it. Remember to transmit this password to the users in a separate communication, do not put it in the same communication that you use to transmit the certificate!

When users wants to import the certificate into their browser they will have to enter this password. Note that once the certificate is imported into the browser they will no longer have to supply the password. It’s one time only.

Certificate flow summary

Setup a CA Generate self signed CA

Client request User creates private key User generates certificate signing request User submits request to CA

CA receives request from user Issue certificate

User converts certificate to a p12 file Combine certificate and private key into PKCS#12 format



Apache configuration

Now that you have issued the client certificate it’s time to configure Apache to support client certificates.

The core of the configuration lies in SSLVerifyClient, SSLCACertificateFile and SSLVerifyDepth. You set the certificate verification level with SSLVerifyClient and with SSLCACertificateFile you list the file containing the certificates of the allowed CAs. With SSLVerifyDepth you define how deeply the verification should go before deciding a certificate is valid or not.

SSLVerifyClient require SSLCACertificateFile /etc/apache2/myca/ca.cer SSLVerifyDepth 10 CustomLog ${APACHE_LOG_DIR}/access.log "%h %l %{SSL_CLIENT_S_DN_Email}x %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" <Location /> SSLOptions +FakeBasicAuth +StrictRequire SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ and %{SSL_CLIENT_S_DN_O} eq "MyOrg" and %{SSL_CLIENT_S_DN_OU} eq "MyDpt") </Location>

As you can see in the config file I also added a custom log handler, CustomLog. This allows you to track which users connected. In this case I used SSL_CLIENT_S_DN_Email but you can also use SSL_CLIENT_S_DN_CN.

The Location part limits who can access the website. With SSLRequire you can limit access based on couple of certificate parameters. You can for example limit on organization (SSL_CLIENT_S_DN_O), organizational unit (SSL_CLIENT_S_DN_OU) but also on the supplied user name (common name, SSL_CLIENT_S_DN_CN).

Debugging client certificate access

LogLevel

By default the Apache log file will not return that much useful information when something does not work as expected with client side certificate authentication. You should increase the log level to get more verbose information. Add this to the Apache configuration

LogLevel debug

SSL3_GET_CLIENT_CERTIFICATE

I configured client certificate authentication with personal certificates received from www.digicert.com. This worked fine with Chrome and Safari but failed when using Firefox.

Although the allowed CA was properly set I got this error message

SSL Library Error: error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate -- No CAs known to server for verification?

In order to solve the problem, I had to merge the certificate CA file and the certificate chain file into one file. For using client certificates with www.digicert.com this meant

cat TrustedRoot.crt >> MergedCA.crt cat DigiCertCA.crt >> MergedCA.crt

and pointing SSLCACertificateFile to MergedCA.crt