Using client certificates in place of passwords on a public web app

670 Views Asked by At

I have a web app where anyone can register for an account. Instead of passwords, I'd like to use client certificates for authentication. Is this practical?

It seems like there are a lot of problems:

  • How do you generate a keypair in a cross-browser way? <keygen> seems useful, but I need to support Internet Explorer.

  • How do you sign the keypair and get a certificate into the browser? Every explanation of client certs only includes openssl commands to generate a certificate. I really don't want to shell out to openssl from my web application.

  • Since the server is signing certificates, what if its signing (CA) key is compromised? This seems to be a more serious attack than a password database leak, because the attacker can silently impersonate any user forever.

    I'd like to avoid signing a certificate at all, and use the just the public key fingerprint as the user's identity. This means that knowledge of the CA key can't be used to impersonate anyone, because they don't have users' private keys.

  • It seems like someone should have done all this before. Is there any open-source implementation of the whole end-to-end process? A Python or Django implementation would be particularly helpful.

Here's what I think the process would look like:

  1. While the user is signing up, their browser generates a keypair and sends a signing request to the server.
  2. The server immediately returns with a signed certificate that gets loaded into the browser. The server stores the key fingerprint in the database to authenticate the user later. I don't want to use the certificate DN as the authentication, see above about CA compromise.
  3. The frontend webserver requires clients certs, validates them, and sends the public key fingerprint to my app server.
  4. The app server looks up the user by their key fingerprint, and are now authenticated.

I don't want to deal with certificate revocation. Since the key fingerprint is used to authenticate, not the certificate DN data, just changing the fingerprint associated with a user will force the use of that key.

EDIT: I was able to find a demo implementing a similar workflow: https://openweb.or.kr/html5/index_en.php. It doesn't work in IE, and has the CA problems I talked about.

1

There are 1 best solutions below

0
On BEST ANSWER

Is this practical?

Handling certificates has a learning curve for most users, including people who work in IT. Storing, moving and protecting these certificates can be a pain. (I wrote something on this topic on this question on Security.SE a while ago.)

How do you generate a keypair in a cross-browser way? seems useful, but I need to support Internet Explorer.

How do you sign the keypair and get a certificate into the browser? Every explanation of client certs only includes openssl commands to generate a certificate. I really don't want to shell out to openssl from my web application.

You may find some helpful pointers in this question. In particular, you can generate the keypair with ActiveX in IE. Then, implement a server-side CA. In Java, BouncyCastle is very useful for this, and I suppose pyOpenSSL would be useful in Python too.

Since the server is signing certificates, what if its signing (CA) key is compromised? This seems to be a more serious attack than a password database leak, because the attacker can silently impersonate any user forever.

Indeed.

I'd like to avoid signing a certificate at all, and use the just the public key fingerprint as the user's identity. This means that knowledge of the CA key can't be used to impersonate anyone, because they don't have users' private keys.

You will have to sign your certificates for them to be valid X.509 certificates, but you can tweak the parties verifying those certificates to ignore the signature and rely on the public key. The fact that the client has the private key matching the certificate's public key is guaranteed by the CertificateVerify message in the TLS handshake, and it's rather independent from the layer verifying the certificate itself.

Apache Httpd and the Java will both let you accept certificates without verifying them during the handshake itself (or will let you customise how this is done), using SSLVerifyClient optional_no_ca or a empty TrustManager respectively, but you must remember to perform some verification later on. In particular, be careful not to mix this application with other applications that rely on the normal PKI verification to be done by the default handlers, otherwise those would be insecure.

There are various problems associated with all this (in particular which certification authorities list is to be sent by the server, or how to choose a client certificate). You'll find more details about this in this answer.