konstantint / eid-webauth-samples

eID Smart Card Web Authentication Samples
42 stars 14 forks source link

eID Web Authentication Samples

Estonian ID card is the official identification document in Estonia. It is a smart card, and as such can be used to authenticate to web services. This repository contains example implementations of ID-card based authentication for various platforms.

Note that the official documentation has some useful materials on this topic. You are free to refer to them if the documentation here seems insufficient.

Preliminaries

Estonian ID card

The ID card (EstEID) is an ISO/IEC 7816 smart card. As such it contains a chip with two private keys (one meant for authentication and one for signing) and two corresponding certificates. A very thorough technical specification of the card is given here (reading it is optional if you only need to set up web-based authentication, however).

SSL/TLS

SSL (also known as TLS or SSL/TLS) is a network communication protocol, which uses asymmetric cryptography to establish a secure channel. It is familiar to most Internet users in the form of HTTPS ("HTTP over SSL"), which is a protocol used to securely serve most privacy-sensitive websites in the Internet nowadays.

In its most familiar form, connection security in SSL is established by authenticating the server to the client. For that the server sends to the client its public key. This key can be used by the client to encrypt its messages to the server and proceed with secure communication. In order to guarantee to the client that the provided public key indeed belongs to the server (rather than to an imposter), the public key is accompanied by a certificate. A certificate is a document, which claims "this public key belongs to this domain" and which is signed using a private key belonging to some third party. This party is known as a Certification Authority (CA) and it is trusted by the client to not issue false claims. Sometimes the public key of the CA is further signed by a higher-level CA, which may in turn be signed by a further CA, all the way to the Root CA, which is trusted unconditionally. A list of such trusted Root CA-s is predefined in the client's browser. Consequently, any key used in SSL communications must be associated with a chain of signatures (certificate chain), linking it to a trusted Root CA. Whenever this is not the case (i.e. the root CA is not trusted), the browser will issue warnings about insecure connections.

SSL client certificate authentication

The SSL protocol allows to use the same mechanism to authenticate the client. That is, the server may request the client to prove that he owns some public key along with a certificate chain linking this key to some Root CA trusted by the server. If this succeeds, the server may use the information in the certificate to identify the user. This process of client certificate authentication forms the basis for using an ID card in web authentication. Namely, in order for such authentication to happen:

Configuring ID-card based authentication

ID-card authentication is nothing more than requesting (on the server side), providing (on the client side) and accepting (on the server side again) the corresponding certificate from the smart card during the SSL handshake phase. As SSL is a transport-level protocol, in most web application platforms the details of this handshake are usually not under control of the web application and have to be configured at the server level.

In the following we describe the steps needed to set up the server along with example applications in PHP, NodeJS, Python and Java. In addition we provide example setups in the subdirectories of this repository packaged as docker containers. See the README in the corresponding directories.

Note that the examples and sample code are only meant as initial guidelines. Do not copy those blindly and make sure you understand all the security implications of particular set up options for your project. In particular, please take time to read through the following paper before pushing your application into production:

Creating a server certificate

No matter which platform you choose, you will need to create a server keypair along with a certificate, proving the ownership of your domain name. Note that for the certificate to be trusted by the client's browser (so that he would be able to access your site without seeing the security warnings), you must obtain one from a certification authority that is trusted by the browsers. Unfortunately, most such CAs provide their services at a price. Estonia's own Sertifitseerimiskeskus offers such a service. Some cheaper possibilities could be explored here. For most start-up and personal projects the best option is to obtain a certificate for free from StartSSL. Finally, at development time you may resort to using a self-signed certificate, which is easy to generate without the need to contact any external parties.

The steps needed to be taken to create a certificate signed using a trusted CA service are usually well documented on the site of the corresponding CA. If you need a self-signed certificate instead, you can generate it as follows (change "yourdomainname.com" to the actual domain name):

openssl genrsa -out /etc/ssl/private/server.key 2048

openssl req -new -key /etc/ssl/private/server.key -out server.csr -subj "/C=EE/ST=Harjumaa/L=Tallinn/O=Test/OU=Test Department/CN=yourdomainname.com" openssl x509 -req -days 365 -in server.csr -signkey /etc/ssl/private/server.key -out /etc/ssl/eid/server.crt rm server.csr

No matter which way you go, you will end up with two files: server.key (the private key) and server.crt (the public key along with the certificate). Those files will be necessary in further steps. If you feel confused about the process of certificate generation, the Internet provides multiple well-written guides. One nice example is:

Enabling SSL in the server

Once you have the server.key and server.crt, you need to configure the server to use those in SSL communications. In Apache you would expect to start with a configuration block like the following:

<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        DocumentRoot /var/www/html

        SSLEngine on

        SSLCertificateFile    /etc/ssl/eid/server.crt
        SSLCertificateKeyFile /etc/ssl/private/server.key
    </VirtualHost>
</IfModule>

The configuration concepts for other servers are analogous. It is worth noting, however, that at the current moment Apache seems to be the best option for configuring ID-card-based client authentication. Nginx has some issues which make such a configuration very inconvenient. See the README in the nginx subdirectory.

Enabling client certificate authentication

In order for the server to accept Estonian ID card certificates you need to configure it to trust the corresponding root CA certificates. Those must be first downloaded and concatenated together into a single file:

wget https://sk.ee/upload/files/EE_Certification_Centre_Root_CA.pem.crt

wget https://sk.ee/upload/files/ESTEID-SK_2011.pem.crt wget https://sk.ee/upload/files/ESTEID-SK_2015.pem.crt cat EE_Certification_Centre_Root_CA.pem.crt ESTEID-SK_2011.pem.crt ESTEID-SK_2015.pem.crt > ca.crt rm EE_Certification_Centre_Root_CA.pem.crt ESTEID-SK_2011.pem.crt ESTEID-SK_2015.pem.crt

NB: this list of commands will become outdated eventually.

Review https://sk.ee/en/repository/certs/ to make sure you download all the necessary certificates

The result is a ca.crt file, containing all eID root certificates concatenated. For the server to use this file it must be added to the host configuration block:

SSLCACertificateFile /etc/ssl/eid/ca.crt

Next you should configure which accesses to the server should trigger a certificate request, and whether presenting a valid certificate is required for the connection to proceed. For example:

<Location />
    SSLVerifyClient optional
    SSLVerifyDepth 3
</Location>

The data of the provided certificate will be accessible in your PHP or CGI scripts via the environment variables SSL_CLIENT_S_DN and SSL_CLIENT_VERIFY if you enable the +StdEnvVars option as follows:

<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>

Dealing with certificate revocation

Issued certificates may be revoked before their official validity end date -- this happens when an ID card gets lost or stolen. Consequently, every time a client authenticates with a certificate, a separate check needs to be done to ensure the certificate has not been revoked. There are multiple ways of implementing such checks.

CRLs

The "simple" way is based on certificate revocation lists (CRLs), that are regularly published by the certification authorities. To enable certificate checking against CRLs you need to first download them to a single directory:

wget http://www.sk.ee/crls/eeccrca/eeccrca.crl

wget http://www.sk.ee/repository/crls/esteid2011.crl wget http://www.sk.ee/crls/esteid/esteid2015.crl

Convert them to PEM format:

openssl crl -in eeccrca.crl -out eeccrca.crl -inform DER

openssl crl -in esteid2011.crl -out esteid2011.crl -inform DER openssl crl -in esteid2015.crl -out esteid2015.crl -inform DER

Create symlinks of the form <file hash>.r0 --> file.crl (alternatively, you could concatenate them all into a single file):

ln -s eeccrca.crl `openssl crl -hash -noout -in eeccrca.crl`.r0

ln -s esteid2011.crl openssl crl -hash -noout -in esteid2011.crl.r0 ln -s esteid2015.crl openssl crl -hash -noout -in esteid2015.crl.r0

Let your webserver know about the directory where the CRLs are stored (alternatively, you could use the SSLCARevocationFile directive pointing to concatenated certificates):

SSLCARevocationPath /etc/ssl/eid/crl

Finally, and most importantly, create a script that would regularly re-download the new CRLs and reload the server. You need to configure this script to run automatically at frequent enough intervals (e.g. using cron). An example script renew_crl.sh is provided in the apache subdirectory of this project. It will need tuning to your taste. Note that if your CRLs become outdated, Apache will deny connections completely.

OCSP

CRLs are only updated at fixed intervals, hence in the time between a certificate revocation and the re-download of the updated CRL the revoked certificate will still be considered valid by the server, which is a security risk. To avoid this problem we may directly contact the CA each time a certificate is being presented to the server and check the current validity status of the certificate. Such a validity confirmation can be requested using the Open Certificate Status Protocol (OCSP) from the OCSP service provided by the CA.

The benefit of this approach is apparent, as it makes all certificate validations up to date and removes the need to keep track of the CRLs. The main drawback is that the SK.ee OCSP service requires a contract and although it is currently free while in pilot use, it might become a paid service in the future. During development you may use a test OCSP service provided by the SK.

In theory, you should be able to enable OCSP by simply configuring the webserver as follows:

SSLOCSPEnable on
SSLOCSPDefaultResponder http://ocsp.sk.ee/_auth
SSLOCSPOverrideResponder on

In practice, due to this Apache bug and the particularities of eID OCSP service, this will not work. Instead, you will need to implement OCSP checks at the application level. Starter code, illustrating how this could be done via openssl is provided in apache/www/ocsp.php.

LDAP

Another option for verifying certificate validity is to query the certification centre's LDAP database. Ideally it should only contain data about the currently valid certificates. The benefit of this approach is that it is free and, similarly to OCSP, results in up to date validity status confirmation. The main drawback is the "unofficial" status of this method. Namely, the certification centre has not taken any formal obligations nor given any guarantees that such usage of the LDAP service is valid, that the certificates in LDAP would always be up to date or that the service would always be accessible. Example code is provided in apache/www/ldap.php.

Login/logout

The SSL handshake is usually not under control of the application. Besides that, there are currently no standards, dictating how could the browser be forced to "forget" the authenticated user (although some work is ongoing). Because of that, implementing convenient login/logout functionality in a cross-browser manner upon SSL client authentication can be tricky, if not impossible. Indeed, most applications out there seem to ignore the problem whatsoever and are notorious for requiring browser (and sometimes even computer) restarts when you need to re-authenticate to them with a different ID card or when your first authentication attempt failed and you want to retry. This project does not provide a bulletproof solution either (yet hopefully gives enough detail to inspire smarter implementations in the future). Consider the example code in apache/login.

Client setup

Obviously, for the authentication process to work the user must install appropriate drivers and have EstEID root certificates added to the trusted store of his browser(s). This would normally happen when the user installs ID-software. If you encounter issues with Firefox, check this post.

Sample applications

The remaining part of this project contains several sample applications which should help you better understand the concepts above. See the README files in the corresponding subdirectories for further guidance.

Note that, in general, for platforms like node or j2ee you might eventually be better off configuring an Apache front-end proxy to handle SSL rather than torturing the platform's native capabilities. The python directory provides an example of such a proxy set up.

License