I got mine through the e-Residency programme, which is designed to let non-Estonians have a recognized digital identity. E-residents don’t have the rights and responsibilities real residents have, but they do get to establish companies, file taxes, open bank accounts, and digitally sign documents and contracts, among other things.
Becoming an e-Resident was pretty simple. The process goes something like this: Fill in an online form, pay the fee (about €50), and wait for the Politsei- ja Piirivalveamet to do a background check. When you get an email (in Estonian, Russian, and English) that your identity card is ready to be picked up, phone the embassy for an appointment to leave your fingerprints. And that’s it. You’ll get the ID card, a pair of PIN codes, a PUK code, and a tiny USB reader. I went for ice cream on the way back home, but that’s entirely optional.
From a developer’s point of view, the Estonian ID card is ‘just’ a PKCS#15 smartcard with a X.509 client certificate (there are some RSA keys on there as well, for signing). This means that anyone and everyone can use it to identify users. There’s no permission required, no red tape to cut through.
And that’s why I got one: to see how hard it would be to write an app that uses the eID for authentication. Not hard, as it turns out. As an example, let’s build an app that shows a friendly (personalized!) greeting to everyone who logs in.
There are instructions on the e-Residency website for using Apache/PHP, and the wiki also has instructions for IIS. I like
nginx more than either, though, so here’s a (very) minimalistic nginx config:
nginx to listen for SSL (TLS) connections on the HTTPS port. The
server.crt files are your usual SSL certificates: they serve to prove the server’s identity to the client. You would generate the first yourself, and get the second from a vendor. For our little example app, a self-signed certificate will do.
ssl_client_certificate setting is more interesting. It points to a bundle of certificates; only if the client certificate
nginx receives is signed by one of these certificates is the request allowed to go through. The certificates are published by SK, and to turn them into a bundle, you just have to concatenate them:
Next, we tell
nginx that we want it to always require a client certificate by using the
ssl_verify_client option, and that we want it to dig up to three levels deep to get the certificate validated.
Enabling SSL client certificate verification enables a handful of interesting nginx variables, including
$ssl_client_cert, which contains the entire certificate,
$ssl_client_verify, which should be SUCCESS, and
$ssl_client_s_dn, which contains the subject’s distinguished name. For eID certificates, the distinguished name contains the person’s name. Which is exactly what our app needs!
The Flask app we’ve got waiting on port 8080 is quite simple. It reads the X-Client header we had
nginx set, extracts the GN (given name) and SN (surname) from the DN (distinguished name), and shows a friendly greeting, just as we wanted.
And here (drumroll, please!) is the end result:
I’d say that’s pretty neat. Besides the first and last name, you get a
serialNumber that’s guaranteed to be unique across all the cards - you could use that as a unique user identifier if you wanted.
(By the way, if you’re using Django instead of Flask, check out django-ssl-client-auth. It’s designed for, or at least tested with, Finnish government-issued ID cards, but from what I’ve seen eID cards should work as well.)
Our little example app isn’t really production-ready. For one thing, we don’t handle certificate revocation. When an ID card gets lost or stolen, the certificate that’s on the card gets revoked - but it will still be usable in our app.
Lists of certificates that have been revoked, called CRLs, are published by SK, and you could periodically fetch those. However, this is where things get a little more complicated.
nginx has a setting for loading a CRL file, but not for multiple CRL files. Concatenating CRLs in the same way as CA certificates is something that should work, but in my case it didn’t. Your mileage may vary. An alternative to CRL files is OCSP, however,
nginx does not support OCSP for client certificates, and there are no plans to add it.
Photo of Karula National Park by Amadvr, CC BY-SA 3.0 ee
Thanks for reading! If you have any questions, comments or corrections, feel free to shoot me an email.