Certificados FNMT en Linux Mini-HOWTO

Copyright. Licencia

Versión 1.0 29-Dic-2005

Este documento es Copyright (©) 2005 de Juan Antonio Martínez Castaño < jonsito _at_ teleline _dot_ es >
Puede copiarse, modificarse y distribuírse libremente por cualquier medio siempre y cuando se mantenga esta nota de copyright y se incluya un enlace al sitio web original

Introducción

Con el advenimiento del DNI digital y tal, en múltiples foros se repite la mismas preguntas:

La documentación que existe en internet es cuando menos oscura. Despues de mucho investigar, he aquí un pequeño mini-howto. No está ni mucho menos completo, pero al menos sirve como introducción.
Cualquier sugerencia será siempre bien recibida y convenientemente agradecida

Primeros pasos: Instalando el certificado raíz

Asumimos que todo el mundo que lea este documento sabe como:

Del mismo modo asumo que el lector tiene claro lo que es un certificado digital y para qué sirve.... y que tiene algo de experiencia, o al menos se maneja con los manuales de OpenSSL

El procedimiento esta muy bien detallado y documentado en la web de la Casa, http://www.cert.fnmt.es , por lo que no me voy a enrrollar demasiado...

Preparando el entorno

Antes que nada, vamos a crear un directorio ${HOME}/.certs que nos será muy util a la hora de guardar todos los certificados y hacer un par de guarrerías

En el navegador

Cuando en la página web de la FNMT pulsas en "Descargar Certificado Raíz" Firefox pregunta diligentemente si lo quieres instalar. Nosotros obedientes decimos que sí... y dejamos todo a medias.

Efectivamente, el certificado se descarga y se instala, pero no se especifican los usos a que se puede destinar. Deberemos pues hacer lo siguiente
Editar - Preferencias - Avanzadas - Administrar certificados - Autoridades

Aparecerá una lista de autoridades de certifcación. Buscamos la de la FNMT, la seleccionamos y marcamos las tres casillitas, que nos "activarán" a la FNMT como Autorida de Certificación

fnmt_firefox.png

Si no hacemos esto, el certificado estará simplemente cargado, pero no servirá para nada....

En Evolution, Acrobat Reader, OpenOffice, etc

La descarga del certificado raiz de la FNMT tiene un problemita: solo se instala en el navegador. !Pero nuestro Linux es mucho más que un navegador!. Necesitamos instalar el certificado raíz en muchas otras aplicaciones....

Por consiguiente ahora vamos a hacer un pequeño truco: nos volvemos a la página web de la FNMT y en lugar de pulsar en "Descargar certificado", le damos al boton derecho y seleccionamos "Guardar como"... y acto seguido nos guardamos el fichero FNMTClase2CA.crt, que diligentemente guardaremos como ${HOME}/.cert/FNMTClase2CA.crt

El fichero que hemos descargado, realmente tal y como viene no nos vale para casi nada. Está en un formato binario llamado DER que se usa comúnmente en criptografía. La mayor parte de las aplicaciones no saben usar los ficheros .der, sino que usan el .pem, que es una especie de traducción a Base64 con cabeceras añadidas

El procedimiento de conversión es sencillo:

bash$ cd ${HOME/.certs
bash$ openssl x509 -inform DER -in FNMTClase2CA.crt -text -fingerprint > FNMTClase2CA.pem
En cristiano: le decimos a openssl que va a manejar un certificado, que está en formato .der, que tiene que imprimir información textual sobre el contenido, añadirle el hash y guardarlo en formato .pem
Podéis echar un vistazo al contenido de dicho fichero si tenéis mucho morbo...

Ahora toca irse a cada aplicación y cargar el certificado raíz que hemos guardado antes. Por ejemplo: en Evolution la secuencia de clicks es:
Editar - Preferencias - Certificados - Autoridades - Importar

fnmt_evolution.png

Igual que en el caso anterior, deberemos editar las opciones, para permitir que evolution pueda usar el certificado para firmar, encriptar, etc, etc

Procederemos de modo similar para el resto de aplicaciones que manejen certificados.

Supongo que algún día los desarrolladores unificarán todo esto de manera que la consulta de certificados raíz estén centralizados, pero a día de hoy esto es lo que hay

En OpenSSL

Hasta aquí llega lo que todo usuario normal debería hacer, pero nosotros no somos usuarios normales !Nosotros usamos Linux!. Por consiguiente ahora toca realmente meterse en las tripas del sistema. Vamos a arreglar el OpenSSL para que _todas_ las aplicaciones que usen la libssl funcionen con la FNMT. ( apache, ssh, etc)

El procedimiento consiste en, como en los casos anteriores, añadir el certificado raíz de la FNMT al OpenSSL. Lo primero que hay que hacer es localizar dónde guarda OpenSSL la lista de certificados raíz.

En Fedora Core 4 el culpable está en /etc/pki/tls/certs/ca-bundle.crt
El procedimiento es sencillo:

root# cp /etc/pki/tls/certs/ca-bundle.crt /etc/pki/tls/certs/ca-bundle.crt.orig
root# cat /path/to/FNMTClase2CA.pem >> /etc/pki/tls/certs/ca-bundle.crt

En otras distribuciones, lo siento, pero tendréis que investigar un poco hasta localizar el fichero.

Si habéis llegado hasta aquí, perfecto: ya tenéis vuestro Linux preparado y listo para manejar ya certificados emitidos por la FNMT

Un último apunte: las versiones modernas de muchas aplicaciones ya tienen incluídos los certificados raíz de la FNMT. Habrá que verificar pues si está o no instalado, y en su caso activar las opciones de uso de dicho certificado.

Instalando nuestro certificado

Una vez que ya nuestro Linux reconoce los certificados que emite la FNMT, vamos al meollo: que reconozca NUESTRO certificado :-)

Adaptando el certificado a Linux

Nos volvemos a encontrar con un problema similar al anterior: la Casa nos ofrece un certifcado en formato .pem, pero la mayor parte de las aplicaciones que manejan certificados importan los certificados de usuario en formato p12.... de nuevo viene openssl al rescate.

Supongamos que hemos descargado el certificado y lo hemos guardado como ${HOME}/.certs/fnmt_cert.pem. Para exportarlo a formato p12, hacemos:

bash$ openssl pkcs12 -export -in fnmt_cert.pem -out fnmt_cert.p12 -name "Mi Certificado FNMT"
Se nos preguntará por una contraseña, que sirve para desencriptar el contenido del fichero. Es altamente recomendable no olvidarla nunca :-)

Exportando el certificado desde el navegador

En algunas versiones del navegador, éste se pasa de listo, y en lugar de guardar el certificado que descargamos en un fichero, se lo queda "p'a la saca". !pero nosotros queremos usar el certificado en otras aplicaciones!. No problemo, vamos a exportarlo:
Editar - Preferencias - Avanzadas - Administrar Certificados

Seleccionamos nuestro certificado y en la opción "Hacer copia de seguridad" seleccionamos el fichero destino. ( El navegador añade automaticamente la extensión .p12 )

Se nos preguntará una contraseña, que es la que se usará a partir de ahora para abrir dicho fichero. Como siempre, mejor no olvidarla...

El penúltimo paso: Casi todas las aplicaciones usan el fichero .p12, con una excepción: OpenSSL. Es necesario pasar dicho fichero a formato .pem. De nuevo OpenSSL acude al rescate:

bash$ openssl pkcs12 -in fnmt_cert.p12 -clcerts -out fnmt_cert.pem
La opción -clcerts es crucial: por defecto el certificado que nos asigna la FNMT incluye no solo nuestro certificado, sino una copia del certificado raíz de la FNMT. Cuando openssl busca en el fichero de certificados que le damos, tiene la mala costumbre de por defecto usar el primero que encuentra, lo que indefectiblemente hace que intente usar el certificado de la FNMT en lugar del nuestro -que está a continuación en el fichero-.

Podéis probar a comparar los ficheros resultantes de incluír o no esta opción.

Importando nuestro certificado en las aplicaciones

Como hemos comentado anteriormente, Linux no tiene todavía un cert-store centralizado, por lo que es preciso importar nuestro certificado en cada aplicación.

A título de ejemplo veamos como se hace en Evolutión:

evolution_import

Manejando certificados con OpenOffice

OpenOffice es un caso extraño: al menos la versión que yo utilizo, soporta el manejo de certificados.... importándolos desde mozilla.

En teoría si Mozilla/firefox esta correctamente instalado, no debería haber problemas, y en las opciones de firma de documentos te encuentra los certificados.
En la práctica es necesario, si no existe, exportar la variable MOZILLA_CERTIFICATE_FOLDER de manera que OpenOffice la utilice para localizar nuestro profile activo del navegador. Esto ocurre en el caso de, por ejemplo, cuando se tienen varios profiles, y los certificados se encuentran en uno de ellos, o bien, como es mi caso, cuando solo se tiene Firefox y no Mozilla

Programando aplicaciones web

Hasta aquí lo necesario para la mayor parte de los usuarios de Linux. Los programadores de aplicaciones web que se agarren los machos...

Usando window.cryptoSignText()

La cosa no puede ser más simple.

El primer fichero que se adjunta es un formulario que pide un texto, y al aceptar se firma dicho texto con nuestro certificado:


<html>
<head>
<title>PKI login form</title>
<script language="javascript">
function signAndSend() {
    document.login.firma.value=crypto.signText(document.login.nick.value,"ask");
    document.login.submit();
}
</script>
</head>
<body>
<form name="login" method="post" action="login.php">
<input type="text" name="nick" value="">
<input type="hidden" name="firma" value="">
<input type="submit" value="Entrar" onClick="javascript:signAndSend();">
</form>
</body>
</html>

El segundo fichero es algo más elaborado: se trata de un script php que coge el formulario anterior, y verifica la firma, presentando en caso de validación, el contenido de algunos campos del certificado digital presentado


<?php

  function saveMsg($msg,$file) {
    $fp = fopen($file,"w");
    fwrite($fp,$msg);
    fclose($fp);
  }

  function saveSig($sig,$file) {
    $data = "-----BEGIN PKCS7-----\n".$sig."\n-----END PKCS7-----\n";
    $fp = fopen($file,"w");
    fwrite($fp,$data);
    fclose($fp);
  }

  // verificacion a traves de exec("openssl.... ")
  function verifySig($msgFile,$sigFile,$certFile) {
	// this is valid for Fedora Core 4. change as needed
	$ca_dir="/etc/pki/tls/certs";
	$ca_certs="/etc/pki/tls/certs/ca-bundle.crt";
	// extract certificate
	exec("openssl pkcs7 -inform PEM -in $sigFile -print_certs -outform PEM -out $certFile", &$salida, &$res);
	if($res!=0) {
		echo "Error en extracción de certificado\n";
		return $res;
	}
	// certificate is valid, now check signature
	exec("openssl smime -verify -inform pem -in $sigFile -content $msgFile", &$salida,&$res);
	return $res;
  }

   // verificacion con openssl_pkcs7_verify()
   // parece ser que segun los manuales de php tiene algun fallo...
  function verifySig2($msgFile,$sigFile,$certFile) {
	// this is valid for Fedora Core 4. change as needed
	$ca_dir="/etc/pki/tls/certs";
	$ca_certs="/etc/pki/tls/certs/ca-bundle.crt";
	$res= openssl_pkcs7_verify($sigFile,0,$certFile,array($ca_dir,$ca_certs));
	return $res;
  }

?>
<html>
</head>
<title>Verificacion de firma</title>
</head>
<body>
Bienvenido <? echo $_POST["nick"]; ?>
<br/>
<?php
$login = $_POST["nick"];
$sig = $_POST["firma"];
// $sig="kkdvak";

// initialize tmp files
$msgFile= tempnam("/tmp","Msg");
saveMsg($login,$msgFile);
$sigFile= tempnam("/tmp/","Sig");
saveSig($sig,$sigFile);
$certFile = tempnam("/tmp/","Cert");

$rv = verifySig($msgFile,$sigFile,$certFile);
if ($rv==0) {
 	echo "Verificacion de firma Aceptada</br>\n";
	$cert_info = openssl_x509_parse("file://$certFile");
	echo("Common name: '".$cert_info['subject']['CN']."'<br>\n");
	echo("E-mail: '".$cert_info['subject']['Email']."'<br>\n");
} else {
 	echo "Verificacion de firma fallida<br>\n";
}

// TODO: verificar por que pugnetas esto no funciona
// $rv = verifySig2($msgFile,$sigFile,$certFile);
// if ( $rv === false) {
// 	echo "Verificacion de firma fallida<br>\n";
// } else if ($rv === -1) {
// 	echo "Error en proceso de verificación<br>\n";
// } else {
// 	echo "Verificacion de firma Aceptada";
//	$cert_info = openssl_x509_parse("file://$certFile");
//	echo("Common name: '".$cert_info['subject']['CN']."'<br>\n");
//	echo("E-mail: '".$cert_info['subject']['Email']."'<br>\n");
// }

  // clear workspace
  unlink($msgFile);
  unlink($sigFile);
  unlink($certFile);
?>
</body>
</html>

Como habréis podido adivinar, PHP tiene un ligero fallo: parece ser que en función del navegador, del OpenSSL y del PHP, la función openss_pkcs7_verify() no siempre funciona. Este error está documentado y reportado, pero no he sido capaz de encontrar una solución, por lo que lo dejo comentado en el código

Usando tarjetas criptográfics

Por escribir.

De momento, y para los osados , os paso dos enlaces:

  1. http://www.musclecard.com/
  2. http://www.opensc.org

Apendices

  1. Tenéis toda esta documentación en formato .tgz aquí
  2. Aquí estan los ficheros login.html y login.php
  3. La página web de la Casa de la moneda es http://www.cert.fnmt.es
  4. Podéis encontar el OpenSSL en http://www.openssl.org