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.
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"> </form> |
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 $.ajaxOpciones 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() { |
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ónica (PDF)</h2> <h3>Seleccione el archivo que va a firmar electró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
- 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.
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() |
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ónica (PDF)</h2> <h3>Seleccione el archivo que va a firmar electró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 |
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()
|
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ónica (PDF)</h2> <h3>Seleccione el archivo que va a firmar electró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.