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
This is a incredibly well written howto!!!
Thanks a lot – it saved me many hours of struggling …
Regards, John
Very clarifying article indeed. I’m just wondering why you are using the dataEncipherment as keyUsage?
kr,
Phil
Thanks. Your clear explanation helped me set up secure client authentication on my server.
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! 🙂
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!
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).
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!
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.
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
Limit access via SSLRequire / Require for (replacement of allow/deny)
SSLVerifyClient require
SSLVerifyDepth 10
SSLCACertificateFile /opt/ssl/cacert.pem
added these too, still no use, just application go through without certificates
Thanks for this documentation, is perfect and works fine.
Great Document, But need one more help.
How to Revoke Client Certificate in case the client certificate has been compromised.?
Thanks
great doc thank you very much
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.
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
I have the same issue, not sure how to combine basic auth with client side certs…
Have you solved it?