Cinque Terre

Saltar la navegación

Contenido 2: Firmar Electrónica de Documento PDF

Objetivos

En esta sección tiene como objetivo conocer el proceso de firma electrónica Web que usa el sistema Murachí y realizar un ejercicio practico del consumo del servicio Firmar electrónica PDF.

Conocimiento previo

  • Manejo de protocolo web
  • Nociones básicas de programación
  • Programación web

Actividad

Firma electrónica de documento usando el formato PDF

Antes de realizar el ejercicio es necesario conocer el proceso de firma electrónica web que utiliza el sistema Murachí. El diagrama de secuencia muestra el proceso de firma.

Figura 1. Diagrama de secuencia sobre el proceso de firma electrónica usando el formato PDF


Como se puede observar en el diagrama de secuencia del lado del cliente se van a realizar 3 procesos:

  • Subir documento pdf: En este paso se carga al sistema Murachí el archivo usando el recurso “:/Murachi/0.1/archivos/” y se obtiene como respuesta el identificador único del archivo pdf (:identificador_unico_de_archivo_pdf).
  • Preparar firma del archivo: En este caso se envía el certificado firmante al sistema Murachí junto con otros parámetros necesarios para la firma electrónica. En esta ocasión se utiliza  el complemento de firma esteidfirefoxplugin (Instalado en la sección de configuración del ambiente de prueba) a través de la biblioteca de JavaScript hwcrypto (para mayor información puede visitar JavaScript hwcrypto) con el recurso :Obtener_certificado(getCertificate), para obtener del servidor el hash del archivo PDF (:hash_a_cifrar).
  • Completar la firma del archivo:  En este paso se va a enviar al sistema Murachí el hash cifrado utilizando el recurso :Murachi/0.1/archivos/pdfs/resenas, para completar la firma electrónica. Aquí se utiliza el complemento de firma esteidfirefoxplugin a través de la biblioteca de JavaScript hwcrypto para acceder al dispositivo criptográfico y cifrar con la clave privada protegida por el dispositivo criptográfico el hash que se obtuvo en la sección anterior, usando el recurso :firmar_hash(sign). Obtenemos del sistema Murachí el identificador único del archivo firmado.
Como el servicio de firma electrónica se realiza en varios pasos vamos a dividir el ejercicio en secciones.

Sección 1: Subir documento pdf

En la primera sección se envía al sistema Murachí el archivo pdf que se va a firmar. Entonces para esta sección se requiere de un formulario que permita tomar del sistema de archivo documentos pdf y luego enviarlo con el método $.ajax usando el recurso /Murachi/0.1/archivos/

Característica del formulario HTML

Control:

  • Botón de envío (Submit button)

  • Botón de reinicializando (Reset button)

  • Selección de fichero (file select)

Atributos:

  • action: función de javaScript que procesa el formulario para esta actividad la función es “SignFilePDF()”

  • enctype: este atributo específica el tipo de contenido que sera usado para enviar los datos al servidor, para esta actividad se utilizará “multipart/form-data”

Código del formulario HTML

<form enctype="multipart/form-data" action="javaScript:SignFilePDF()" method="post" id="firmar" name="SignFormat">
     <h2>Firmar electr&oacute;nica (PDF)</h2>
     <h3>Seleccione el archivo que va a firmar electr&oacute;nicamente</h2>
     <br>
     <input id="file-sign" class="file" type="file" data-min-file-count="1" name="upload" accept=".pdf" >
     <br>
     <button type="submit" class="btn btn-primary">Enviar</button>
     <button type="reset" id="reset"  class="btn btn-default">Limpiar</button>
     <br>
     <br>
     <div id="seccion1"> </div>
     <br>
     <div id="seccion2"> </div>
     <br>
     <div id="seccion3"> </div>

</form>

Los elementos <div> del formulario con id = "sección1", “sección2y “sección3” se utiliza para mostrar los resultados correspondientes a los 3 procesos descritos para la firma electrónica (consumo del servicio a Murachí).

Función SignFilePDF() Sección 1. Subir documento pdf

En esta sección la función procesa los datos del formulario. En tal sentido, primero se obtiene el archivo seleccionado en el formulario a través del método document.getElementById("file-sign") donde “file-sign” corresponde al id del elemento input de tipo “file” del formulario. Luego se almacena en un objeto de tipo FormData que permite el envío del archivo al servidor usando el método $.ajax

Opciones de configuración de la petición $.ajax:

  • ulr: Establece la URL en donde se realiza la petición, para esta sección es "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos".

  • type: Establece el tipo de petición, para esta actividad vamos a utilizar "POST".

  • dataType: Establece el formato de la respuesta que es permitido, si el servidor devuelve información con un formato diferente al especificado el código fallará. Para este proceso se establece “json”.

  • data: Establece la información que se enviará al servidor. Para este proceso se envía el archivo almacenado en una variable de tipo FormaData.

  • contentType: Establece el tipo de codificación que se va a utilizar, para esta actividad es "application/json".

  • processData: Establece si la información que se envía al servidor debe ser procesada a una cadena de caracteres. Para evitar esto se debe utilizar el valor “false”. En esta sección sólo se quiere enviar el archivo sin ser procesado.

  • xhrFields y headers: ya indicado al principio de esta sección.

Código de la función javaScript "SignFilePDF()" sección 1

function SignFilePDF() {
     var fileInput = document.getElementById("file-sign");
     var list = fileInput.files;
     var form = $('firmar')[0];
     var data = new FormData();
     data.append('upload', $("#file-sign")[0].files[0]);
     $.ajax({
         url: "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos",
         type: "post",
         dataType: "json",
         data: data,
         cache: false,
         contentType: false,
         processData: false,
         xhrFields: {withCredentials: true},
         headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
         success: function(response) {
         var responseString = JSON.stringify(response);
         document.getElementById("seccion1").innerHTML = responseString;
         }
     })
}

La repuesta de esta sección se procesa por el método .done a través del argumento response donde podemos acceder al valor del JSON con la función JSON.stringify.


Código completo de la sección 1:

<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="gitversion.js"></script>
<script type="text/javascript" src="hwcrypto-legacy.js"></script>
<script type="text/javascript" src="hwcrypto.js"></script>
<script type="text/javascript" src="hex2base.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">

function SignFilePDF() {
    var fileInput = document.getElementById("file-sign");
    var list = fileInput.files;
    var form = $('firmar')[0];
    var data = new FormData();
    data.append('upload', $("#file-sign")[0].files[0]);
    $.ajax({              
        url: "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos",
        type: "post",
        dataType: "json",
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        xhrFields: {withCredentials: true},
        headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
        success: function(response) {
            var responseString = JSON.stringify(response);                                      
            document.getElementById("seccion1").innerHTML = responseString;
        }
    })
}
</script>
</head>
<body>
<form enctype="multipart/form-data" action="javaScript:SignFilePDF()" method="post" id="firmar" name="SignFormat">
    <h2>Firmar electr&oacute;nica (PDF)</h2>
    <h3>Seleccione el archivo que va a firmar electr&oacute;nicamente</h2>        
    <br>
    <input id="file-sign" class="file" type="file" data-min-file-count="1" name="upload" accept=".pdf" >
    <br>
    <button type="submit" class="btn btn-primary">Enviar</button>
    <button type="reset" id="reset"  class="btn btn-default">Limpiar</button>
    <br>
    <br>
    <div id="seccion1"> </div>
    <br>
    <div id="seccion2"> </div>
    <br>
    <div id="seccion3"> </div>
</body>
</html>

La respuesta del servicio Murachí es el identificador único del archivo pdf, la respuesta es un json.
Ejemplo: {"containerId":"b43ea1f0-2c1a-463e-a0b8-1804e2041f33"}

Sección 2: Preparar firma del archivo

En esta sección se va a calcular el valor del hash del documento que se envió en la sección 1, en tal sentido antes del cálculo del hash se debe enviar al servidor Murachí toda la información que se agregará al documento tales como el certificado del firmante como información adicional necesaria para la firma electrónica:
  • fileId: corresponde al identificador del archivo que se encuentra en el servidor y se desea firmar (identificador único del archivo pdf).
  • certificate: corresponde al certificado del firmante en formato hexadecimal.
  • reason: corresponde a la razón de la firma (cadena descriptiva del por qué de la firma).
  • location: corresponde a la ubicación donde se realiza la firma.
  • contact: corresponde a información de contacto del firmante.
  • signatureVisible: true para mostrar un indicador visible de firma en la primera página del documento pdf y false para no mostrar un indicador visible de firma en la primera página del documento pdf.
El certificado lo debemos obtener desde el dispositivo criptográfico, para la misma se utiliza el complemento de firma “esteidfirefoxplugin”  a través de la biblioteca de JavaScript hwcrypto y que finalmente se envía al servicio Murachí junto con los parámetros y el identificador único del contenedor.

Para trabajar con la librería JavaScript hwcrypto, nuestra página HTML debe realizar transferencias de datos seguros (https://), para la misma se debe instalar un certificado que respalde la identidad del servidor (https://servidordebian.org/es/squeeze/intranet/ssl_cert/self_signed). Posteriormente se configura el servidor apache2 para que tenga soporte a transferencia de datos seguros (soporte a ssl) (https://servidordebian.org/es/squeeze/internet/http/apache2_ssl).
Con la librería hwcrypto se accede al certificado del firmante que se encuentra en el dispositivo criptográfico usando el recurso :Obtener_certificado(getCertificate), para luego enviarlo al sistema Murachí con el metodo $.ajax usando el recurso :Murachi/0.1/archivos/pdfs

Código de la función javaScript "SignFilePDF()" sección 2:

document.getElementById("seccion1").innerHTML = responseString;

var fileId = response.fileId.toString();

var cert;

window.hwcrypto.getCertificate({lang: "en"}).then(function(response) {

     var cert = response;

     var parameters = JSON.stringify({

         "fileId":fileId,

         "certificate":cert.hex,

         "reason":"Certificado",

         "location":"CENDITEL",

         "contact":"582746574336",

         "signatureVisible":"true"

         });

     $.ajax({

         type: 'POST',

         contentType: 'application/json',

         url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs",

         dataType: "json",

         data: parameters,

         xhrFields: {withCredentials: true},

         headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},

         success: function(data, textStatus, jqXHR) {

             var responseString = JSON.stringify(data);

             document.getElementById("seccion2").innerHTML = responseString;

         } // respuesta ajax 2

     }) //ajax 2

}) //windowhwcrypto.getCertificate()

Si la petición tiene éxito la repuesta de esta sección se procesa a través de la función success: donde se le pase tres parámetros, data: que corresponde al resultado de la petición formateado según el valor del parámetro dataType (respuesta del servidor), textStatus: que corresponde a una cadena que describe el estado y jpXHR: que es un objeto que permite ejecutar varias funciones.

Código completo de la sección 2 (Asegure de hacer la prueba usando https://)

<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="gitversion.js"></script>
<script type="text/javascript" src="hwcrypto-legacy.js"></script>
<script type="text/javascript" src="hwcrypto.js"></script>
<script type="text/javascript" src="hex2base.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">

function SignFilePDF() {

    var fileInput = document.getElementById("file-sign");
    var list = fileInput.files;
    var form = $('firmar')[0];
    var data = new FormData();
    data.append('upload', $("#file-sign")[0].files[0]);

    $.ajax({              
        url: "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos",
        type: "post",
        dataType: "json",
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        xhrFields: {withCredentials: true},
        headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
        success: function(response) {
            var responseString = JSON.stringify(response);                                      
            document.getElementById("seccion1").innerHTML = responseString;
            var fileId = response.fileId.toString();
            var cert;

           window.hwcrypto.getCertificate({lang: "en"}).then(function(response) {
               var cert = response;
               var parameters = JSON.stringify({
                   "fileId":fileId,
                   "certificate":cert.hex,
                   "reason":"Certificado",
                   "location":"CENDITEL",
                   "contact":"582746574336",
                   "signatureVisible":"true"
               });
              $.ajax({
                  type: 'POST',
                  contentType: 'application/json',                
                  url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs",
                  dataType: "json",
                  data: parameters,
                  xhrFields: {withCredentials: true},        
                  headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
                  success: function(data, textStatus, jqXHR) {
                  var responseString = JSON.stringify(data);
                  document.getElementById("seccion2").innerHTML = responseString;
        } // respuesta ajax 2
                   }) //ajax 2
              }) //windowhwcrypto.getCertificate()
       } // respuesta ajax 1
    }) //ajax 1
}
</script>
</head>
<body>
<form enctype="multipart/form-data" action="javaScript:SignFilePDF()" method="post" id="firmar" name="SignFormat">
    <h2>Firmar electr&oacute;nica (PDF)</h2>
    <h3>Seleccione el archivo que va a firmar electr&oacute;nicamente</h2>        
    <br>
    <input id="file-sign" class="file" type="file" data-min-file-count="1" name="upload" accept=".pdf" >
    <br>
    <button type="submit" class="btn btn-primary">Enviar</button>
    <button type="reset" id="reset"  class="btn btn-default">Limpiar</button>
    <br>
    <br>
    <div id="seccion1"> </div>
    <br>
    <div id="seccion2"> </div>
    <br>
    <div id="seccion3"> </div>
    <br>
    <div id="seccion4"> </div>
</form>
</body>
</html>


Respuesta:

Field Type Description
hash String Reseña o hash del archivo que se debe cifrar con la clave privada protegida por el dispositivo criptográfico.

La respuesta del sistema Murachí es el valor del hash del archivo pdf que se subió al servidor en la sección 1.

Ejemplo :{"hash":"a3fbfc857e0796b5e5e9fdb8e3183d8e532afe0fe662f3a24eb459f0c22bd472","error":""}



Posibles errores del servicio Murachí:

HTTP/1.1 400 Bad Request
{
  "hash": "",
  "error": "El archivo que desea firmar no es un PDF."
}

HTTP/1.1 401 Unauthorized
{
  "error": "acceso no autorizado"
}

HTTP/1.1 500 Internal Server Error
{
  "hash": "",
  "error": "error en carga de certificado de firmante"
}

Posibles errores al usar la librería JavaScript hwcrypto:

* "user_cancel" - El usuario a cancelado la operación.
* "no_certificates" - No hay certificado disponible para el complemento de firma.
* "technical_error" - Error técnico inesperado del complemento de firma
* "no_implementation" - No se ha encontrado el complemento de firma (No instalado)
* "not_allowed" - ejecución no permitida por no usar https
* "pin_blocked" - El dispositivo criptográfico esta bloqueado

Sección 3: Completar la firma del archivo
En esta sección se firma el hash del documento a firmar con el certificado firmante almacenado en el dispositivo criptográfico. En tal sentido partimos desde el resultado que obtuvimos en la sección 2 (el valor del hash del archivo pdf).
El cifrado del hash se realiza dentro del dispositivo criptográfico; para la misma vamos a volver a utilizar el complemento de firma “esteidfirefoxplugin” a través de la biblioteca de JavaScript hwcrypto usando el recurso :firmar_hash(sign). Finalmente se envía el hash cifrado con el método $.ajax al sistema Murachí para completar la firma del archivo pdf

Función SignFilePDF() Sección 3. Completar la firma del archivo

(Se parte desde la sección anterior donde se obtuvo el hash del archivo)

Opciones de configuración de la petición $.ajax:

  • url: Establece la URL en donde se realiza la petición, para esta sección es "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs/resenas".

  • type: Esatblece el tipo de petición, para esta sección vamos a utilizar "POST".

  • dataType: Establece el formato de la respuesta que es permitido, si el servidor devuelve información con un formato diferente al especificado el código fallará. Para esta sección se establece “json”.

  • data: Establece la información que se enviará al servidor. Para esta sección se enviá el hash que se obtiene de la siguiente manera: JSON.stringify({"signature":signature.hex}).

  • contentType: Establece el tipo de codificación que se va a utilizar, para esta sección es "application/json".

  • xhrFields y headers: ya indicado al principio de esta sección.

Código de la función javaScript "SignFilePDF()" sección 3:

document.getElementById("seccion2").innerHTML = responseString;

var json_x = data;

var hash = json_x['hash'];

alert("hash recibido del servidor "+hash);

var hashtype = "SHA-256";

var lang = "eng";

window.hwcrypto.sign(cert, {type: hashtype, hex: hash}, {lang: lang}).then(function(signature) {

    $.ajax({

         type: 'POST',

         contentType: 'application/json',

         url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs/resenas",

         dataType: 'json',

         data: JSON.stringify({"signature":signature.hex}),

         xhrFields: {withCredentials: true},

         headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},

         success: function(data, textStatus, jqXHR){

             var responseString = JSON.stringify(data);

             document.getElementById("seccion3").innerHTML = responseString;

             alert('Archivo firmado correctamente: ' + data['signedFileId']);

            document.getElementById("seccion4").innerHTML = "Descargar archivo firmado: https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/descargas/" + data['signedFileId'];

         } // repuesta del ajax 3

     }) //ajax 3

}) //windowhwcrypto.sign()

 

Si la petición tiene éxito la repuesta de esta sección se procesa a través de la función success: donde se le pase tres parámetros, data: que corresponde al resultado de la petición formateado según el valor del parámetro dataType (respuesta del servidor), textStatus: que corresponde a una cadena que describe el estado y jpXHR: que es un objeto que permite ejecutar varias funciones.


Código completo de la sección 3

<html>
<head>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="gitversion.js"></script>
<script type="text/javascript" src="hwcrypto-legacy.js"></script>
<script type="text/javascript" src="hwcrypto.js"></script>
<script type="text/javascript" src="hex2base.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">

function SignFilePDF() {

    var fileInput = document.getElementById("file-sign");
    var list = fileInput.files;
    var form = $('firmar')[0];
    var data = new FormData();
    data.append('upload', $("#file-sign")[0].files[0]);

    $.ajax({              
        url: "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos",
        type: "post",
        dataType: "json",
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        xhrFields: {withCredentials: true},
        headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
        success: function(response) {
            var responseString = JSON.stringify(response);                                      
            document.getElementById("seccion1").innerHTML = responseString;
            var fileId = response.fileId.toString();
            var cert;

        window.hwcrypto.getCertificate({lang: "en"}).then(function(response) {
            var cert = response;
            var parameters = JSON.stringify({
                "fileId":fileId,
                "certificate":cert.hex,
                "reason":"Certificado",
                "location":"CENDITEL",
                "contact":"582746574336",
                "signatureVisible":"true"
            });
            $.ajax({
                type: 'POST',
                contentType: 'application/json',                
                url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs",
                dataType: "json",
                data: parameters,
                xhrFields: {withCredentials: true},        
                headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
                success: function(data, textStatus, jqXHR) {
                var responseString = JSON.stringify(data);
                document.getElementById("seccion2").innerHTML = responseString;

                var json_x = data;
                var hash = json_x['hash'];
                alert("hash recibido del servidor "+hash);
               var hashtype = "SHA-256";
               var lang = "eng";

               window.hwcrypto.sign(cert, {type: hashtype, hex: hash}, {lang: lang}).then(function(signature) {

                  $.ajax({
                      type: 'POST',
                      contentType: 'application/json',
                      url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs/resenas",
                     dataType: 'json',
                     data: JSON.stringify({"signature":signature.hex}),
                     xhrFields: {withCredentials: true},
                     headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
                     success: function(data, textStatus, jqXHR){
                     var responseString = JSON.stringify(data);
                     document.getElementById("seccion3").innerHTML = responseString;
                     alert('Archivo firmado correctamente: ' + data['signedFileId']);
                     document.getElementById("seccion4").innerHTML = "Descargar archivo firmado: https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/descargas/" + data['signedFileId'];    
                  } // repuesta del ajax 3
              }) //ajax 3
          }) //windowhwcrypto.sign()
        } // respuesta ajax 2
     }) //ajax 2
   }) //windowhwcrypto.getCertificate()
} // respuesta ajax 1
}) //ajax 1
}
</script>
</head>
<body>

<form enctype="multipart/form-data" action="javaScript:SignFilePDF()" method="post" id="firmar" name="SignFormat">
    <h2>Firmar electr&oacute;nica (PDF)</h2>
    <h3>Seleccione el archivo que va a firmar electr&oacute;nicamente</h2>        
    <br>
    <input id="file-sign" class="file" type="file" data-min-file-count="1" name="upload" accept=".pdf" >
    <br>
    <button type="submit" class="btn btn-primary">Enviar</button>
    <button type="reset" id="reset"  class="btn btn-default">Limpiar</button>
    <br>
    <br>
    <div id="seccion1"> </div>
    <br>
    <div id="seccion2"> </div>
    <br>
    <div id="seccion3"> </div>
    <br>
    <div id="seccion4"> </div>
</form>
</body>
</html>


La respuesta del servicio Murachí sería el identificador único del archivo firmado.

Ejemplo:

{"signedFileId":"847948fb-a5d5-46d3-8176-9c474b22e57b-signed.pdf"}



Para descargar el archivo del sistema Murachí se agregar la dirección https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/descargas/ más el identificador único del archivo firmado.

Ejemplo: https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/descargas/847948fb-a5d5-46d3-8176-9c474b22e57b-signed.pdf


Se copia este enlace y se coloca en un navegador para descargar el archivo desde el servidor Murachí.

Puede descargar el código en el siguiente enlace: FirmaElectronicaPDF.html

Obra Publicada con Licencia Creative Commons Reconocimiento – No Comercial – Compartir Igual – 3.0 Venezuela (https://creativecommons.org/licenses/by-nc-sa/3.0/ve/deed.es_ES)

Material Educativo basado en los resultados del proceso de investigación y desarrollo del Proyecto Seguridad Informática de la Fundación Centro Nacional de Desarrollo e Investigación en Tecnologías Libres (CENDITEL), ente adscrito al Ministerio del Poder para Educación Universitaria, Ciencia y Tecnología, ubicada en Mérida – Venezuela.