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
Con el advenimiento del DNI digital y tal, en múltiples foros se repite la mismas preguntas:
Asumimos que todo el mundo que lea este documento sabe como:
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...
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
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
Si no hacemos esto, el certificado estará simplemente cargado, pero no servirá para nada....
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.pemEn 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
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
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
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.
Una vez que ya nuestro Linux reconoce los certificados que emite la FNMT, vamos al meollo: que reconozca NUESTRO certificado :-)
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 :-)
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.pemLa 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.
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:
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
Hasta aquí lo necesario para la mayor parte de los usuarios de Linux. Los programadores de aplicaciones web que se agarren los machos...
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
Por escribir.
De momento, y para los osados , os paso dos enlaces: