HTTP PKI client certificate

Server-Side Setup

Ubuntu xenial with Apache 2.4

Created your own Certification Authority on e.g. dc1.example.local, then submit certificate requests (CSRs) to that CA for:

Note. we truly need to enable HTTPS on the server itself, as it seems to be a requirement for the PKI client authentication to pass through.

Send those to the server and workatation(s) e.g. for the server,

scp /etc/ssl/ubuntu63.example.local.crt ubuntu63:/etc/ssl/
scp /etc/ssl/private/ubuntu63.example.local.key ubuntu63:/etc/ssl/private/

Enabling HTTPS (Ubuntu xenial with Apache 2.4),

a2enmod ssl
a2enmod headers
vi /etc/apache2/apache2.conf

<VirtualHost *:443>
        Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"
        SSLEngine on
        SSLProtocol all -TLSv1 -SSLv2 -SSLv3
        SSLHonorCipherOrder on
        SSLCertificateFile /etc/ssl/ubuntu63.example.local.crt
        SSLCertificateKeyFile /etc/ssl/private/ubuntu63.example.local.key
        #SSLCertificateChainFile /etc/httpd/ssl/issuer-concat-cert.crt

        ServerName ubuntu63.example.local

        LogLevel warn

        DocumentRoot /var/www/html

systemctl restart apache2

Note. the apache daemon now needs the passphrase for restarting, as it needs to unlock the private key for ubuntu63.example.local.

We can now reach the page normally and through TLS, as the client-side certificate verification is not enabled yet: http://ubuntu63.example.local/.

Now enabling client-side certificate check on the Apache service,

on the CA host

scp /etc/ssl/cacert.pem ubuntu63:/etc/ssl/

on ubuntu63

vi /etc/apache2/apache2.conf #in the vhost

    SSLCACertificateFile /etc/ssl/cacert.pem
    <Location /protected>
      SSLOptions +StdEnvVars
      SSLVerifyClient require

systemctl restart apache2

As a result, when trying to load the /protected folder without any client specific certificate, the http client e.g. Firefox shows,


And the logs on the server shows,

[Tue Mar 13 22:21:05.573195 2018] [ssl:error] [pid 17401:tid 139778410333952]
[client x.x.x.x:45252] AH02261: Re-negotiation handshake failed
[Tue Mar 13 22:21:05.573258 2018] [ssl:error] [pid 17401:tid 139778410333952]
SSL Library Error: error:140890C7:SSL routines:ssl3_get_client_certificate:
peer did not return a certificate -- No CAs known to server for verification?

Warning: one must take good care to disable the service on port 80. Enabling client certificate check on port 443 and keeping port 80 open would not serve any purpose in our goal. Instead of closing the port 80 on the firewalls and web daemon, I rather prefer to setup redirects. Therefore, users that do like to type urls manually into their browser will be able to reach the TLS-enabled service with no hassle nor pain.


Client-Side Setup: enabling the client certificate

Converting the separated .crt and .key to a single PKCS12 certificate for Firefox,

on the workstation with Firefox, as user

openssl pkcs12 -export -in lenovo.example.local.crt -inkey lenovo.example.local.key -out lenovo.example.local.p12 -CAfile /etc/ssl/cacert.pem
chmod 400 lenovo.example.local.p12
ls -lhF lenovo.example.local.*

The -chain argument was not used because we have a simple chain of trust: one CA and one client cert that are directly linked.

We used the same export password for the PCKCS12 certificate as for the passphrase of the private key.

Importing the .p12 into Firefox,

Edit > Preferences
Advanced // View Certificates // Your Certificates
(provide unlock/export password)

Then when trying to access the page again with our Firefox browser, the user will be prompted on what certificate to provide to the server:


and the page displays fine when selecting the previously imported certificate.