Buscar este blog

domingo, 24 de abril de 2016

@Firma - Firma trifase con Autofirma 1.4.2

Según se indica en su página oficial, @firma es una plataforma de validación y firma electrónica multi-PKI desarrollada por el MINHAP, y que se pone a disposición de las Administraciones Públicas, proporcionando servicios para implementar la autenticación y firma electrónica avanzada de una forma rápida y efectiva.

Originariamente @firma sólo ofrecía un applet de firma para hacer firma en cliente. De este modo las aplicaciones incluían una serie de librerías y javascript en sus páginas para ejecutar el este applet, que era el que se encargaba de acceder a los certificados del usuario.

Con la progresiva caída en desuso de los applets se ha potenciado el uso de un componente de usuario denominado Autofirma. Se trata de una aplicación independiente que debe instalar manualmente el usuario y que es quien en última instancia realiza la firma.

El caso más básico de funcionamiento consiste en que desde la página web se invoque el componente de autofirma pasándole el fichero que se desea firmar (en base 64), se firme en local, y luego se envíe de vuelta a la página. De ahí, volvería finalmente al servidor.

Este modelo de integración es válido cuando los ficheros son relativamente pequeños, pero para documentos mayores implica un tráfico de red excesivo. Además, en algunos dispositivos móviles no se permite la descarga de documentos, como por ejemplo en iOS.

Para solventar estos problemas se dispone de la opción de firma trifase. En estos casos, el documento nunca llega a salir del servidor remoto y la firma se realiza de forma conjunta entre autofirma y una nueva aplicación web denominada servidor trifase.

Cuando desde página web se invoca a autofirma (a través de invocación por protocolo), se le pasa como parámetro la URL del servidor trifase y un identificador que hace referencia al fichero remoto que se quiere firmar. Autofirma establece una comunicación con el servidor trifase, indicándole este identificador y se intercambian mensajes para componer una firma e incrustrarla en en el documento resultante.

En la siguiente imagen se muestra el diagrama general de funcionamiento.


El componente autofirma y el server trifase se pueden descargar de la página de la forma del CTT, http://forja-ctt.administracionelectronica.gob.es/web/clienteafirma. Desde ahí se puede ir a la página de la forja.

Además, el código fuente del server trifase (como el del resto del proyecto) se puede consultar en su github https://github.com/ctt-gob-es/clienteafirma.

Para hacer pruebas lo mejor es descargar el server trifase y modificar su config.properties para que emplee acceda a una ruta de disco, que servirá de entrada y salida de los ficheros firmados. El fichero de configuración está en WEB-INF/classes/config.properties:
# Origenes permitidos
Access-Control-Allow-Origin=*

# Clase DocumentManager
#document.manager=es.gob.afirma.triphase.server.document.SelfishDocumentManager
document.manager=es.gob.afirma.triphase.server.document.FileSystemDocumentManager

# Instalar provedor de XMLdSig alternativo
alternative.xmldsig=false

# Configuracion de la clase FileSystemDocumentManager
indir=D:/tmp/afirma/entrada
outdir=D:/tmp/afirma/salida
overwrite=true

Luego basta con desplegarlo tal cual en un servidor de aplicaciones, por ejemplo tomcat.

Al descargar el componente de autofirma también se incluye una página para probar todas las opciones disponibles de firma. He cogido esa página y la he recortado para dejar únicamente la parte de firma trifase.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html> <!-- Ejemplo basico de lanzador de la aplicacion -->
  <head>
 <title>Ejemplo de despliegue del MiniApplet @firma</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
 <script type="text/javascript" src="miniapplet.js"></script>
 <script type="text/javascript">

  function doSign() {
   try {
    MiniApplet.sign(
     document.getElementById("idFicheroServidor").value,
     "SHA512withRSA",
     "PAdEStri",
     document.getElementById("params").value,
     showResultCallback,
     showErrorCallback);
    
   } catch(e) {
    try {
     showLog("Type: " + MiniApplet.getErrorType() + "\nMessage: " + MiniApplet.getErrorMessage());
    } catch(ex) {
     showLog("Error: " + e);
    }
   }
  }
  
  function showResultCallback(signatureB64, certificateB64) {
   showLog("Firma OK");
   document.getElementById('result').value = signatureB64;
   document.getElementById('certificate').value = certificateB64;
  }
 
  function showErrorCallback(errorType, errorMessage) {
   showLog("Type: " + errorType + "\nMessage: " + errorMessage);
  }
    
  function showAppletLog() {
   try {
    showLog(MiniApplet.getCurrentLog());
   } catch(e) {
    showLog("Type: " + MiniApplet.getErrorType() + "\nMessage: " + MiniApplet.getErrorMessage());
   }
  }
  
  function cleanDataField(dataField, textDiv) {
   textDiv.innerHTML = "";
   dataField.value = null;
  }
  
  function addExtraParam(extraParam) {
   var paramsList = document.getElementById("params");
   paramsList.value = paramsList.value + extraParam + "\n";
   document.getElementById('newParam').value = "";
  }
  
  function cleanExtraParams() {
   document.getElementById("params").value = "";
   document.getElementById('newParam').value = "";
  }
  
  function showLog(newLog) {
   document.getElementById('console').value = document.getElementById('console').value + "\n" + newLog;
  }
 </script>
  </head>
 <body>
  <script type="text/javascript">
   MiniApplet.setForceWSMode(false);
   MiniApplet.cargarAppAfirma();
  </script>

  
  <fieldset><legend>Entrada de datos</legend>
  <div>
    <span>Identificador de fichero en servidor:</span> <input id="idFicheroServidor" type="text" value="prueba.pdf">      
  </div>
  </fieldset>
  <br/>
  
  <fieldset><legend>Configuraci&oacute;n de la firma</legend>  
   <div>
     <label for="newParam">ExtraParams</label>
     <input id="newParam" type="text"><input type="button" value="Agregar" onclick="addExtraParam(document.getElementById('newParam').value);">&nbsp;
     <input type="button" value="Limpiar" onclick="cleanExtraParams();">&nbsp;
     <span>(Insertar las propiedades de una en una)</span>
     <br>
    <textarea id="params" cols="50" rows="5" readonly>serverUrl=http://localhost:8080/afirma-server-triphase-signer/SignatureService
    </textarea>
   </div>
  </fieldset>
  <br/>

  <input type="button" value="Firmar" onclick="doSign();">&nbsp;  
  <input type="button" value="Mostrar Log" onclick="showAppletLog();">
  <br/>
  
  <div>
   <span>Consola</span>
   <br>
   <textarea id="console" cols="150" rows="10">
   </textarea>
  </div>
  
  <div>
   <span>Resultado</span>
   <br>
   <textarea id="result" cols="150" rows="10">
   </textarea>
  </div>
  
  <div>
   <span>Certificado</span>
   <br>
   <textarea id="certificate" cols="150" rows="10">
   </textarea>
  </div>
 </body>
</html>

Esta página invoca la firma trifase de tipo PAdES (firma de PDF).


El único parámetro que hay que cubrir es el identificador del fichero, que será el nombre de un fichero que se encuentre en el directorio de entrada configurado dentro del server trifase.

Para ver los mensajes intercambiados entre autofirma y el server trifase se puede utilizar un filtro de log de tomcat como el describía en una entrada anterior http://trabajosdesisifo.blogspot.com.es/2016/03/tomcat-custom-request-dump-filter-log.html

11 comentarios:

  1. Hola,

    ¿Dónde se encuentra la página de prueba que permite probar este firmado en modo triphase? No encuentro el ejemplo que aparece en el proyecto de signer. Por otro lado, el ejemplo que se muestra hace referencia a miniapplet. Realmente no se está usando un applet, ¿verdad?

    ResponderEliminar
  2. Hola

    La página de pruebas original se puede obtener de la forja: http://administracionelectronica.gob.es/ctt/clienteafirma/descargas#.WC39wtXhBpg. En el Área de descargas, en el apartado binarios de Prueba, viene junto con el MiniApplet v1.4 (http://administracionelectronica.gob.es/ctt/resources/Soluciones/138/Area%20descargas/MiniApplet%20v1-4.zip?idIniciativa=138&idElemento=6496).


    El componente no requiere del uso del applet, pero sí lleva uno "por si acaso". Es decir, cuando se importa el javascript de carga del cliente de firma se puede configurar de modo que siempre use Autofirma o que, si no detecta Autofirma que intente cargar el applet.
    La forma que tiene el javascript de saber si el cliente tiene Autofirma o no es haciendo llamadas AJAX a localhost, ya que Autofirma publica un sevicio HTTPS en un puerto prenegociado.

    Un saludo

    ResponderEliminar
    Respuestas
    1. Muchísimas gracias por tu respuesta. Según lo que comentas, ¿si tienes autofirma, tira directamente de autofirma y si no la tienes, entonces intenta el applet?. Yo buscaba el comportamiento inverso, que intente usar el applet y si el navegador no se lo permite, que intente invocar autofirma. De todas formas, ahora mismo estoy obteniendo un error que dice:
      "Ha ocurrido un error realizando la operación.
      (SAF_11: Error enviando la firma al servidor remoto)".
      En mi caso, tengo montados en local dos tomcat, uno en el puerto 8080 y otro en el 8081. En el del puerto 8080 tengo desplegado afirma-ui-miniapplet-deploy (que me lo he descargado del github https://github.com/ctt-gob-es/clienteafirma.git) y en el del puerto 8081, tengo desplegado afirma-server-triphase-signer que me lo he descargado del mismo repo en github.

      En la página de pruebas miniapplet-full.html, he hecho que el valor de la variable javascript data sea siempre el nombre de un fichero que tengo en C:/firmatest/in y en el fichero config.properties, he puesto esto:
      # Configuracion de la clase FileSystemDocumentManager
      indir=C:/firmatest/in
      outdir=C:/firmatest/out
      overwrite=true

      También he modificado mi fichero de hosts para que la ip de mi máquina local (127.0.0.1) la mapee contra myhost en lugar de contra localhost porque la documentación que he visto indicaba que de esa forma, usando localhost o 127.0.0.1, se capaba el acceso.

      No consigo encontrar la configuración adecuada para que funcione completamente el firmado.

      Te agradezco muchísimo de antemano tu ayuda.

      Eliminar
    2. Perdona que haga una puntualización más. El error que indico en la entrada anterior (Error enviando la firma al servidor remoto) lo obtengo si hago la prueba en internet explorer, dejándome seleccionar certificado y mostrando el siguiente resultado en el log:


      Type: java.lang.Exception
      Message: _html__head__title_Apache Tomcat/7.0.47 - Error report_/title__style__!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--__/style_ _/head__body__h1_HTTP Status 404 - /afirma-signature-retriever/RetrieveService_/h1__HR size="1" noshade="noshade"__p__b_type_/b_ Status report_/p__p__b_message_/b_ _u_/afirma-signature-retriever/RetrieveService_/u__/p__p__b_description_/b_ _u_The requested resource is not available._/u__/p__HR size="1" noshade="noshade"__h3_Apache Tomcat/7.0.47_/h3__/body__/html_

      Sin embargo, el mismo código probado en Firefox en la misma máquina me muestra el error SAF_03: Error en los parámetros de entrada, sin mostrarme la ventana de selección de certificado y mostrando en el log el resultado:


      Type: java.lang.Exception
      Message: No se ha podido extablecer la comunicación entre la aplicación de firma y la página web
      Type: java.lang.Exception
      Message: No se pudo conectar con el servidor intermedio para la recuperacion del resultado de la operacion

      De nuevo, muchas gracias.

      Eliminar
    3. Hola

      El comportamiento del javascript creo recordar que podía configurarse para forzar a usar una u otra opción. De lo que no estoy seguro es de que intente usar primero el applet y luego Autofirma. De todas formas, el javascript de lanzamiento es bastante sencillo así que incluso podrías retocarlo a tu gusto.

      Respecto al problema de la firma, no estoy seguro de la causa de tu error porque no intenté reproducirlo, pero sí me parece raro el uso que haces de la variable "data".
      Según entiendo, esa variable contiene lo que vas a firmar. En el caso más simple sería una cadena en Base 64 con todo el texto (por ejemplo un xml). En la firma trifase, esa cadena representa el identificador del fichero que se va a firmar. Este identificador es algo que le sirve al server trifase para saber qué fichero, de los que pueda haber en su directorio indir, es el que se va a firmar. Si en este directorio dejas un fichero que se llame prueba.pdf, entonces tu ID sería cHJ1ZWJhLnBkZg==, que es lo que iría en "data".

      Yo creo que el problema está en que el server trifase no es capaz de encontrar ese fichero y devuelve un mensaje de error a Autofirma. Intenta ver los logs del server.

      Y lo de explorer puede ser algo derivado del error del server trifase, y que no es capaz de interpretar correctamente el navegador.

      Un saludo

      Eliminar
  3. Buenos días,
    He desplegado en mi maquina virtual centos, que a su vez esta tiene el tomcat donde están alojados el server trifase y el miniapplet y he instalado en mi pc la app cliente de autofirma; la cuestión es que no me queda claro donde le paso desde el miniapplet a autofirma el link de la url de mi server miniapplet que está en mi tomcat. un saludo.

    ResponderEliminar
    Respuestas
    1. Hola

      Hablo de memoria, pero creo que eso se configura desde el javascript que instancia el miapplet (aunque realmente no sea un applet).

      En el ejemplo que ponía, la URL se especificaba a través del parámetro serverUrl, que valía http://localhost:8080/afirma-server-triphase-signer/SignatureService.
      Esta cadena de parámetros es el cuarto argumento de la función MiniApplet.sign().

      Un saludo

      Eliminar
    2. Hola. Gracias por tu respuesta, ahora he desplegado todos los servicios en mi máquina virtual:
      - Server trifase
      - signatureRetriever
      -signatureStoragenecesarios
      Le he pasado la ruta de mi máquina virtual para que vaya al server trifase en el parmetro "param" que efectivamente es el cuarto argumento en la función MiniApplet y le he cambiado la ruta en el constans.js para que vaya a mi máquina y en el miniapplet.js he puesto la constante URL_REQUEST = "https://ipMiMáquina:"; la cuestión es que no consigue lanzar el miniapplet y no se que me estoy dejando por el camino. esto es lo que me sale en el LOG del textarea=#console === CLIENTE LOG ===
      Applet no cargado

      agradecería enormemente si puedes echarme una mano, porque no se que me estoy dejando por el camino o que estoy haciendo mal. un saludo y gracias nuevamente.

      Eliminar
    3. agrego otra anotación, si comento el codigo por defecto de la entrada de datos y pongo en el html el codigo de ejemplo que está arriba :
      * Entrada de datos

      Identificador de fichero en servidor:


      *comento est:


      Pues entonces ya no me invoca a la aplicación, así que creo que ahi esta mi primer problema; no sé si alguien que haya realizado el ejemplo y haya podido firmar en trifase pueda ayudarme. gracias nuevamente.

      Eliminar
    4. Perdona que haga una anotación más, ya he solucionado y me funciona todo bien, sólo quería indicar los pasos que he seguido por si puede servir a alguien en el futuro.
      1- como bien ha dicho en el post sisifo hay que cambiar el properties de afirma-server-triphase-signer como indica en la imagen. Sólo ponerle en mi caso la ruta en mi máquina virtual donde queria recoger para firmar y dejar firmado.
      2- darle a la variable data de la página de prueba miniapplet-full.html el nombre del fichero que se encuentra en la carpeta donde va a ir a buscar lo que se quiere firmar (indicado en el paso 1) yo lo he hecho de la siguiente manera:

      3- Desplegar en mi servicio web los war con la información anterior ya modificada. Hay que desplegar:
      *afirma.war que es el war de la carpeta de pruebas del miniapplet que se descarga del link de descarga
      * afirma-server-triphase-signer.war
      * afirma-signature-storage.war, el que maneja donde dejar la firma
      * afirma-signature-retriever.war, el que maneja de donde coger la firma

      y por supuesto el cliente afirma instalado en la computadora.

      un saludo y gracias por el post, me ha servido de gran ayuda.

      Eliminar
    5. Muchas gracias por tu aportación.
      Espero poder incorporarlo al post en cuanto tenga tiempo.

      Eliminar