Autentificación con Certificado X.509 de Cliente


Passkeys

El futuro prometedor que auguraba la adopción y difusión del uso de Passkeys ha resultado tan distópico como los coches voladores, el metaverso, o incluso Matter. La amigabilidad para el usuario queda reducida a cenizas cuando el entorno de este no se encuentra monopolizado por un único proveedor de servicios. La interoperabilidad entre fabricantes a través de códigos QR y Bluetooth parece una ocurrencia de última hora sin ningún tipo de reflexión.

Pero más allá de las aparentes deficiencias en las distintas implementaciones del protocolo, el concepto mismo de las Passkeys es —una vez más— sólo otra muestra del adanismo de una industria que tiene que reinventar la rueda continuamente ante la falta de objetivos tangibles que mejoren la utilidad. Porque todo esto ya existía hace años. La autentificación con certificados de cliente (CBA), no es sino otro mecanismo de clave pública, equivalente a las Passkeys, aunque personalmente lo considero superior. Desde un punto de vista conceptual, creo que si la autentificación del servidor se produce en la capa de TLS, la del usuario debería establecerse en esa misma operación, para que ambos quedasen segura y mutuamente identificados (mTLS). La implementación de este tipo de soluciones no requeriría el uso de bibliotecas externas y cambios tanto en el backend como en el frontend. El punto débil de la autentificación con certificados de cliente es la generación y gestión de los mismos, pero eso veremos a continuación que no es tan complejo como parece, y tampoco creo que las Passkeys sean mejores en ese aspecto.

Para demostrar el valor de esta CBA me propuse mejorar la seguridad de alguno de mis servidores privados con este método. El mecanismo que usaba anteriormente en nginx es el de autentificación básica de HTTP, perfectamente válido para mis casos de uso aunque infravalorado por la mayoría. Básicamente, el proceso consiste en la creación de una autoridad certificadora (CA) para firmar los distintos certificados de cliente que haya que generar. Todo ello puede hacerse fácilmente con openssl.

DIY

Generamos una clave privada RSA para la CA porque las de curva elíptica me están dando problemas con la importación del PKCS#12 en navegadores.

openssl genpkey -algorithm RSA -out lunaticgeek_ca.key

y su clave pública en formato X.509

openssl req -new -x509 -days 3650 -key lunaticgeek_ca.key -out lunaticgeek_ca.crt

De igual forma se procede con la del cliente

openssl genpkey -algorithm RSA -out edkalrio.key

esta vez, la clave pública irá firmada por la CA, siguiendo el criterio normal de cadena de confianza

openssl req -new -key edkalrio.key -out edkalrio.csr
openssl x509 -req -days 3650 -in edkalrio.csr -CA lunaticgeek_ca.crt -CAkey lunaticgeek_ca.key -set_serial 01 -out edkalrio.crt

empaquetamos el juego de claves del cliente para que pueda instalarse en cualquier dispositivo

openssl pkcs12 -export -clcerts -inkey edkalrio.key -in edkalrio.crt -out edkalrio.p12

La configuración de nginx es trivial. Sólo hay que incluirle el certificado de la CA y activar la función en la sección de server

ssl_client_certificate /etc/ssl/lunaticgeek_ca.crt;
ssl_verify_client on;

Me parece que es un procedimiento bastante sensato y mucho más sencillo que la integración de Passkeys —a juzgar por su baja adopción—. El despliegue de esta solución en un entorno de producción, para un uso generalizado por usuarios finales conllevaría mucha más burocracia, para cumplir con sus requisitos de compliance, pero la parte técnica no cambiaría mucho, más allá de la utilización de un servidor de OCSP para revocar certificados.

One more thing…

Hay una forma incluso más sencilla de usar esta autentificación… y mucho más bizarra. El certificado digital de la FNMT, con el que ya he lidiado en otras ocasiones, es precisamente la materialización de este método para identificarse y firmar documentos con la Administración Pública española. Este certificado se puede usar sin ningún problema en nuestros servidores, sin necesidad de ejecutar ni un sólo comando de openssl. Para ello bastaría con cambiar la referencia de nuestro certificado de CA por el raíz de la FNMT dentro de la configuración de nginx. Al no tener pleno control sobre la CA, si no queremos que cualquiera con certificado digital acceda al servidor, habría que añadir un requisito de acceso, por ejemplo, con el número de serie del certificado, en la configuración de location.

proxy_set_header X-Client-Serial $ssl_client_serial;
if ($ssl_client_serial != "********************************") {return 403;}

Siguiendo la misma lógica, se podría configurar la identificación mediante DNIe, con la consiguiente mejora de seguridad, al estar la clave privada ligada al hardware. Aunque, en cualquier caso, estos certificados se pueden gestionar con llaves de seguridad, exactamente igual que las Passkeys.