Client side certificate authentication

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
    1. Generate self signed CA
  • Client request
    1. User creates private key
    2. User generates certificate signing request
    3. User submits request to CA
  • CA receives request from user
    1. Issue certificate
  • User converts certificate to a p12 file
    1. 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

17 thoughts on “Client side certificate authentication

  1. John Donath on said:

    This is a incredibly well written howto!!!

    Thanks a lot – it saved me many hours of struggling …

    Regards, John

  2. Phil on said:

    Very clarifying article indeed. I’m just wondering why you are using the dataEncipherment as keyUsage?

    kr,
    Phil

  3. Miles on said:

    Spent days trying to work this out, following the failed advice from tens of different sites explaining how to set this up. Your guide worked for me, thanks a bunch! 🙂

  4. Laszlo on said:

    I also get this error in firefox:
    No CAs known to server for verification?

    Can you please explain more how you fix this? What was the order which was working?
    Did you put ca.cer or ca.key into the merged file?

    Thanks!

  5. I merged the certificate CA file and the certificate chain file (both provided by your certificate authority, crt files) into one file and in Apache pointing SSLCACertificateFile to this file. Often you receive the certificate file and you have to download the chain yourself (from the provider).

    • Laszlo on said:

      Thank you for your answer!

      You wrote in the article how to configure client certificate:

      SSLCACertificateFile /etc/apache2/myca/ca.cer

      and now you say I have to use the same SSLCACertificateFile file for merged certificates.
      In this case where should I put the client certificate ca.cer file content?
      thank you!

  6. The latter is if you received a certificate from 3rd party, you have to merge the certificate + the chain in one file and point SSLCACertificateFile to that file.
    ca.cer is if you did self-signed certificate.

  7. kumar ch on said:

    I configured SSL.conf on my client machine like this and it was signed by GoDaddy. As per my understanding whit out this certificate other application should not communicate or transfer data right?
    But without certificates, applications are talking to each other. How to overcome this?

    SSLEngine on
    SSLCertificateFile “/opt/ssl/server-cert.crt”
    SSLCertificateKeyFile “/opt/ssl/private/server-key.pem”
    SSLCACertificateFile /opt/ssl/gd_bubdle crt

  8. Great Document, But need one more help.

    How to Revoke Client Certificate in case the client certificate has been compromised.?

    Thanks

  9. Incrediblestorm on said:

    WOW. I wish I had read this one first. I’ve been bumbling around for a week on this.

    Half the docs have you building the ca in /root, half in /etc/openssl, half in /etc/apache2/ssl. (yea, three halves) Then there is a myriad of configuration discrepancies from whether you use ca or x509 to sign, to what other flags and options are needed. Yours is definitely the best I’ve read so far.

    I’m still trying to figure out the subjectAltName thing, and feel like I’m a bit lost, but I’m making progress understanding it.

  10. Thanks for the effective How-To. It was very great to follow the steps. But I have one issue. besides of the Client authentication I want to use the “standard” basic by User/Password. I still have the .htaccess file in by html-folder.
    The certificate-based Authentication is working fine. But I got follow log line:
    user /C=DE/L=MUC/O=dfdfdfdE/OU=TIM/emailAddress=tim@dfdsf /folder/

    What I have to do, to fix it?
    Adjust the config file?
    Or adding a special user inside of my .htaccess file?

    Thanks in advanced

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.