Lo primero es aclarar que el DNI electrónico no es mas que un certificado mas, al igual que el de la FNMT, el de CamerFirma o el del Colegio de Abogados. Así que el método aquí mostrado vale para cualquiera de las entidades certificadoras que existen en la actualidad.
El paradigma en que se basa la identificación por certificados electrónicos
Pregunta: ¿Cómo identificar en base a un certificado?
Respuesta: Mediante una sesión HTTPS.
Para identificar a los usuarios en base al certificado que presenten, se usa el mecanismo de intercambio de certificados de HTTPS. Esto quiere decir dos cosas:
- No se puede usar este método de identificación en sesiones HTTP sin seguridad.
- Es necesario un certificado de servidor para configurar la sesión HTTPS
En el lado del servidor se suele contar con un certificado expedido por una entidad de las que habitualmente aparecen en los navegadores como entidades de confianza (Verisign, Twate, etc..). Estas entidades verifican la identidad del propietario del certificado, ya sea mediante la documentación que exigen, consultando los registros públicos, u organizando entrevistas con el solicitante. De esta forma son capaces de asegurar al usuario que el sitio al que se conecta pertenece a una persona u organismo que existe y que es accesible. La desventaja es que estas entidades cobran anualmente por la certificación.
Si no se usa una entidad certificadora que exista previamente en el navegador del usuario, este recibirá un error. Para acceder a la aplicación deberá configurar su cliente
para confiar expresamente en el certificado o en sus autenticadores. Esto puede ser aceptable para pruebas o para servicios de nuestra intranet, pero el ofrecer servicios al público con un certificado auto firmado o firmado por una entidad certificadora que no aparezca en la mayoría de los navegadores es muy mala política: Da muy mala imagen sobre nuestro servicio y hace que el acceso a nuestra aplicación sea demasiado complicado para el usuario medio.
En el lado del cliente el funcionamiento es el mismo. Aunque el proceso de confianza o no en el certificado presentado queda automatizado en el servidor: Si el usuario presenta un certificado válido en el que se confía accederá al servicio, si no lo presenta o presenta uno inválido no podrá acceder.
El servidor comprueba toda la ruta de certificación sobre su lista de certificados de confianza. Esto significa que podemos establecer la confianza a nivel de certificados personales o a nivel de autoridades de certificación. Es decir: conceder acceso a personas concretas o a todas las que posean un certificado expedido por una entidad concreta (Todos los DNIs, todos los certificados de FNMT, ect...).
Creación de de una Aplicación WEB de pruebas
Vamos a crear una pequeña aplicación web. En primer lugar vamos a crear un pequeño fichero JSP llamado index.jsp.
<html>
<head>
<title>Pruebas certificados</title>
</head>
<body>
<h1>AuthType = </h1> <%=request.getAuthType() %>
<h1>RemoteUser = </h1> <%=request.getRemoteUser() %>
</body>
<html>
Ahora configuraremos esta página como respuesta a cualquier URL, para ello usaremos el siguiente descriptor de despliegue web.xml.
<web-app >
<servlet>
<servlet-name>controlador</servlet-name>
<jsp-file>/index.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>controlador</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Ahora vamos a especificar una URL en la que se debe solicitar autenticación. Para ello añadiremos lo siguiente al descriptor de despliegue:
<security-constraint>
<display-name>protectedApp</display-name>
<web-resource-collection>
<web-resource-name>secure</web-resource-name>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint >
<role-name>main</role-name>
</auth-constraint>
<user-data-constraint>
<description>SSL required</description>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
</login-config>
<security-role>
<description>access to the application</description>
<role-name>main</role-name>
</security-role>
Expliquemos este último paso.
Hemos definido una security-constraint que responde a la url /secure/ y a todo lo que cuelgue por debajo de ella. A través del tag auth-constraint hemos exigido al usuario que pertenezca al rol "main" para acceder a este directorio. Y finalmente a través del tag user-data-constraint/transport-guarantee exigimos que haya que usar HTTPS para acceder a este directorio, si accedemos por HTTP y todo esta bien configurado el propio servidor nos reenviará al puerto HTTPS.
El tag login-config/auth-method con el valor CLIENT-CERT hace que la autenticación se realice en base al certificado presentado por el usuario.
Finalmente definimos el security-role main . Ya que la especificación exige se haga expresamente para poderlo usar en una security-constraint.
Configuración de Tomcat para aceptar certificados electrónicos.
Lo primero es configurar un puerto para HTTPS. Para ello crearemos un nuevo nodo en el fichero server.xml, bajo el tag < service>.
<Connector port="8443" scheme="https" secure="true" sslProtocol="TLS"
keystoreFile="conf/tomcatKeyStore" keystorePass="hola123"
truststoreFile="conf/tomcatKeyStore" truststorePass="hola123" />
Explicación de los parámetros usados:
- port: El puerto en el que escucha el conector. 8443 esta bien para pruebas, pero el puerto por defecto de HTTPS es 443.
- scheme: Indica el protocolo para el que se configura el puerto. En nuestro caso es HTTPS. El valor de esta opción es devuelto en la aplicación por el método request.getScheme();
- secure: Indica que el puerto sera considerado como seguro. Lo que aquí establezcamos será lo que reciba la aplicación al llamar a request.isSecure();
- sslprotocol: Es el protocolo que se usará para la encriptación y desencriptación de los datos. Algunas implementaciones TLS de JDK 1.4.1 no son compatibles con los navegadores mas populares, si nos da problemas podemos elegir el valor SSL.
- keystorefile: Fichero desde el que se leerá el certificado del servidor. Dentro de él se usa el certificado con alias "tomcat" o el primero disponible. El fichero es buscado en $CATALINA_HOME o en el raíz del sistema si la ruta comienza por / (unix style).
- keystorepass: Contraseña para acceder al fichero de claves. Por defecto Tomcat usa changeit como contraseña si no se le especifica esta opción.
- truststorefile: Fichero desde el que se leerán los certificados de entidades raíces para autentificar usuarios.
- truststorepass: Contraseña para acceder al almacen reseñado en truststorefile.
- En el fichero server.xml por defecto existe un ejemplo comentado de definición de conector, es una buena idea usarlo como plantilla pues añade parámetros muy interesantes sobre el control de la carga de trabajo del servidor.
También haremos caso a la configuración del puerto HTTP para que sepa como reenviar a HTTPS cuando la aplicación exija ser accedida mediante una conexión segura, es decir, cuando se use "SSL required" como <user-data-constraint>. Para ello hemos de añadir la opción redirectPort a la definición del conector HTTP.
<connector port="8080" ... redirectPort="8443"/>
Ahora el paso mas importante: El almacén de los certificados.
Al definir la ruta de los ficheros de almacén hemos usado conf/tomcatKeyStore. Como ya hemos dicho el sistema buscará este fichero en $CATALINA_HOME, esto significa que deberemos dejar el fichero tomcatKeyStore al mismo nivel que los ficheros de configuración de tomcat, o lo que es lo mismo junto a server.xml.
Para trabajar con los almacenes de certificados usaremos la utilidad keytool que viene con el JDK de java.
En unix:
$JAVA_HOME/bin/keytool
En windows
%JAVA_HOME%\bin\keytool.exe
Recomiendo usar almacenes de certificados en formato JKS que es el que por defecto usa java, de esta forma no hay que especificar el formato del almacén constantemente.
Nos queda una cosa: Asociar los diferentes certificados que vayamos recibiendo con un rol concreto. Para ello podemos usar cualquiera de los métodos que soporta tomcat (JAAS, JDBCRealm, LDAP, etc...) Aquí voy a usar el fichero tomcat-users por su sencillez.
Editaremos el fichero conf/tomcat-users para añadir la correlación entre los diferentes certificados y los roles a los que pertenecen. Un ejemplo mas abajo.
<user username='CN="PRUEBEZ PRUEBEZ, PRUEBA (AUTENTICACION)"
givenname="PRUEBA," surname="PRUEBEZ,"
serialnumber="00000000X," c="ES"' roles="main"/>
Crear un certificado auto-firmado para usarlo como certificado de servidor
El proceso es bastante sencillo, basta con un comando:
$JAVA_HOME/bin/keytool -genkey \
-keystore CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -alias tomcat -keyalg RSA
Se han de responder a las preguntas que va realizando. Los valores que facilitemos serán los que conformarán la información del certificado.
Recordemos que Tomcat usa como certificado de servidor el que tiene el alias "tomcat" y que la contraseña del almacén de certificados ha de coincidir con el valor que elegimos en la definición del conector.
Obtener un certificado desde una autoridad de certificación
Iniciamos el proceso creándonos una clave privada.
$JAVA_HOME/bin/keytool -genkey \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -alias tomcat -keyalg RSA
Los valores que especifiquemos serán los que conformen el certificado definitvo, así que mas vale que no nos equivoquemos:
- Common Name (CN): El nombre publico del host. El nombre de DNS (FQDN) con el que los usuarios accederán a nuestro servidor. Tenr en cuenta que si los usuarios acceden a nuestro servidor con un nombre diferente al que aquí usemos, recibirán un error.
- Organizational Unit (OU): El nombre del departamento que gestiona la identificación. Lo habitual es que sea el departamento de informática o el de asesoría legal. Si no sabemos que poner podemos usar el propio nombre de la organización.
- Organization (O): La organización, compañía o persona que será identificada mediante este certificado.
- Locality or City (L): La ciudad dónde esta domiciliada la empresa. Aconsejo usar el domicilio social.
- State or Province (S): La provincia a la que pertenece el municipio antes citado.
- Country Name (C): El código ISO de dos letras del país: ES para España, US para los Estados Unidos, UK para el Reino Unido, RU para Rusia...
Debemos importar el certificado raíz de nuestar entidad certificadora, para ello ejecutaremos el siguiente comando:
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -file intermediateCA.cer \
-alias intermediateCA
Una vez que tenemos el almacén con nuestra clave privada, debemos crear una solicitud de certificado.
$JAVA_HOME/bin/keytool.exe -certreq \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -alias tomcat -file certreq.csr
Esto nos crea un fichero certreq.csr con nuestra petición de certificado. Este es el fichero que hemos de proporcionar a la entidad certificadora junto a nuestra petición.
Una vez que la entidad certificadora haya emitido el certificado definitivo, deberemos importarlo.
$JAVA_HOME/bin/keytool -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -alias tomcat -trustcacerts \
-file <certificate file>
Y ya tendremos nuestro servidor preparado para publicar nuestra aplicación por SSL de una forma confiable para los usuarios.
Importar certificados raíz de las entidades de certificación en las que confiaremos
Otro proceso sencillo gracias a la herramienta keytool.
Ejemplo 1: importación de los certificados raíz de DNI electrónico:
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -file AcRaizDNIe.cer -alias AcRaizDNIe
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -file AcDNIe001.cer -alias AcDNIe001
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore \
-storepass hola123 -file AcDNIe002.cer -alias AcDNIe002
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore
-storepass hola123 -file AcDNIe003.cer -alias AcDNIe003
Ejemplo 2: importación del certificado raíz de FNMT:
$JAVA_HOME/bin/keytool.exe -import \
-keystore $CATALINA_HOME/conf/tomcatKeyStore -storepass hola123 \
-file FnmtCaClase2.cer -alias AcRaizFNMT
Tan solo una cosa: keytool no admite los certificados en formato pkcs1 que podemos obtener directamente de las entidades certificadoras. Necesitamos ese mismo certificado en formato X.509. Yo lo he hecho importándolos en el anillo de claves del sistema y luego exportándolos desde allí en el formato requerido. Si alguien conoce otro método para transformar entre los diferentes formatos de certificados sería de agradecer que lo comentara. ;)

