In this year’s GSoC, I’m co-working on identity.gentoo.org that intends to become a central place for logging in to various Gentoo sites. One of the fancy features that I’d like to implement is SSL-certificate based login.
The client certificate-based authentication is a kind of public key authentication. In order to login, you provide the server with your certificate (containing your public key) and a signature made with your private key. Your private key is never exposed to the server which greatly improves security, and you don’t have to have a store of random passwords.
Unlike many common authentication methods that are implemented in application space, client certificates are part of the SSL/TLS protocols. As a result, they’re supported quite widely by web browsers. But this also introduces a few limitations that will affect the use.
How does it work?
Well, first of all you need to obtain a client certificate. The easy way is to use a CA like cacert.org. The hard way is to do some voodoo using OpenSSL or some other SSL tools. But note that with the latter, you’re going to get a certificate that others will not trust.
When using cacert to generate the certificate, the site generates a certificate request for your browser. Your browser generates the private key and doesn’t disclose it to cacert, just the public key. Then, cacert gives you a nice, signed certificate to download and install. Well, if your browser supports it.
If you’re using the all-so-awesome Chrome/Chromium, start another browser first (Opera and Firefox will work). Generate the key and install the cert as usual. Then export is as PKCS 12 (with the secret key) and import that to Chromium.
Now, whenever you connect to a SSL/TLS-enabled site a handshake is performed. During the handshake, encryption is negotiated and secret that will be used to encrypt the actual data is created. The server authenticates itself with a certificate so that can verify if it’s the server he intended to connect.
Optionally, the server may request the user to authenticate himself with a client certificate. When that happens, all web browsers I’ve tested open a certificate selection pop-up (even if there’s only one certificate installed) and ask user to either choose a certificate or refuse the authentication.
Along with the certificate, a signature made using user’s secret key is sent. At the very least, server checks the signature to ensure that user is eligible to use the certificate. Additionally, server may check whether the certificate was issued by a trusted issuer.
The result of the check along with the client certificate becomes property of the SSL session. It is therefore exposed to the application within each HTTPS request it serves.
How the user is authenticated?
So far we’ve covered the certificate exchange and validation. However, even if we know that the certificate is valid and the client is eligible to use it, we still need to somehow map it to a real user account. There are various approaches to that.
One of the simpler approaches is the 1:1 certificate-user mapping. It is used by certifi.ca for example (though done poorly). In this case, the site stores explicit mapping of client certificates (usually through their fingerprints) to users.
The advantage of this method is that it doesn’t require any specifics from the certificates. The certificates don’t even need to be issued by a trusted CA since the fingerprint requirement provides good enough security. It also gives admin the full control over accepted certificates. The disadvantage is that every time the certificate expires, admin needs to update the fingerprint in the database. For example, certifi.ca failed to support this and leaves your account stale after certificate revocation.
Alternate approach is through dedicated login information. This could some custom data or just reuse of one of the standard fields. It could contain your username, some kind of identifier. This would avoid the above issues since you could generate more certificates with the same login data and you could make the data easily match what’s in the user database.
However, since certificates are basically semi-public, the server needs to be restricted to support only certificates from your CA or otherwise others will be able to create «fake» certificates with the same login data. And since your CA is usually untrusted, your users may have some trouble importing the certificate. So it’s more work for you and your users.
As a special case of the above, the e-mail address can be used. Assuming that each user’s e-mail address is globally unique and that each CA is supposed to verify the e-mail address before creating a certificate using it, it should allow you use to use practically any certificate issued by a trusted CA.
Our site will most likely use the last variant. That will allow us to transparently support client certificates issued by cacert and therefore establish the certificate login with no need for creating our own certificates or maintaining the list of trusted certificates.
When the user will be authenticated?
The another question is when are we going to authenticate the user. The primitive methods, such as password authentication, usually involve dedicated login form and POST submission. However, the certificate authentication needs to be performed during SSL handshake (or renegotiation) and that is done by the HTTP server rather than the application. Therefore, we’re basically limited to what the server permits us to do.
The simpler solution is transparent all-site authentication. That is, to enable the optional client verification for the whole site. The advantage of that method is that the login process is almost immediate for most users. If user with the client certificate installed visits the site, he is instantly asked for it.
Supposedly, user with no client certificates does not even notice the certificate login support. However, user which has client certificates but doesn’t want to use them gets asked for them unnecessarily on every visit.
Worse than that, there’s practically no way of implementing logout or certificate switching this way. If user chooses a wrong certificate or simply wishes to end the session, he actually needs to end the SSL session. No browser I know of allows user to force renegotiation or session shutdown explicitly other way than through closing it (the browser, not just the tab). And the application can’t enforce that either.
Therefore, you’ve got just one shot at it. The site asks for your certificate before you even see it — e.g. Chromium doesn’t allow you to see the site certificate first, so you may end up disclosing privacy sensitive information such as your e-mail address. If you refuse, you won’t be able to login using certificate without closing the browser. If you choose the wrong certificate, you have to close the browser.
The alternative solution is to use dedicated virtual hosts for SSL certificate login and enable certificate verification only on those hosts. That is, put a link like «SSL client certificate login» in the login form, and make it point to another virtual host, with the hostname being unique to the request (e.g. some random identifier). Verify the certificate there, then redirect back.
In this case, client first sees the site. Whenever he wants to login, he needs to explicitly state that he wants to use a client certificate. The element of surprise is gone.
The SSL certificate login is done on dedicated vhost, and the client certificate choice doesn’t leave that domain. That is, as soon as certificate is verified, the user is redirected back to certificate-free zone where the login was purely part of application logic. If user wishes to log out, we just wipe the login from his application. If he wishes to log in again, we give him a new hostname so that he can safely choose the certificate again.
The disadvantage of this method is that it is no longer fast and transparent. You need to explicitly request the login and choose the method. You need to resolve additional hostname and start a new SSL session for a single request — that is a fairly high price for login but likely inevitable one. Plus we need a server certificate with wildcards.
We haven’t yet decided which variant to use but most likely the second one will be preferred. It gives certificate login the flexibility of primitive login methods, so I’d consider it combining the advantages of both.