Firma digital con certificado autofirmado

En este tutorial, se verá la implementación de firma digital con un certificado autofirmado desde una aplicación escrita en NodeJS. Para este propósito, es necesario realizar las siguientes tareas dentro de la aplicación:

  • Generación de par de claves.- Debido a que este tutorial muestra el uso de firma digital con un certificado autofirmado, el primer paso consistirá en la creación del par de claves para realizar la firma digital.
  • Generación del certificado autofirmado.- Un certificado autofirmado no tiene ningún valor legal reconocido por la normativa boliviana, sin embargo la metodología de uso es la misma de un certificado emitido por una entidad certificadora autorizada por lo que viene bien para los propósitos de este tutorial.
    Un certificado no es más que la clave pública con los datos de identidad del signatario firmados para evitar su modificación.
  • Firma de una cadena de texto.- Hasta este punto ya se tiene las herramientas necesarias para poder firmar cualquier documento, ya que la firma no es más que un hash encriptado con la clave privada.

Para la firma y generación de par de claves se utilizará la librería node-forge ya que es muy fácil de utilizar lo que viene bien para este propósito. El algoritmo utilizado para la generación del par de claves será RSA ya que es el aprobado por ATT y en vigencia para la firma de documentos digitales en Bolivia al momento de escribir este tutorial.

Si no tiene instalado NodeJS, puede seguir los pasos descritos en el siguiente enlace: NodeJS

Una vez instalado, ejecutar las siguientes instrucciones para preparar el proyecto y editar el primer archivo del proyecto:

  1. mkdir firmar
  2. cd firmar
  3. npm init
  4. npm install node-forge
  5. nano app.js

Con los pasos anteriores en la consola de comandos, se abrirá un editor de texto donde se puede copiar el siguiente programa:

  1. const forge = require('node-forge');
  2. const keyPair = forge.pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001});

Con estos sencillos pasos, la librería node-forge se encarga de crear el par de claves. El parámetro bits, le indica la longitud que tendrá la clave privada en bits, de acuerdo a la normativa de la ATT el valor mínimo para este atributo es 2048.

La criptografía asimétrica surge de la necesidad de compartir información de forma segura inclusive enviando la clave por el mismo canal de comunicación. Esto se logra haciendo uso de dos claves en lugar de una, de esta manera el destinatario podrá compartir su clave pública con el remitente y el remitente cifrará la información con la clave pública del destinatario para después enviar la información cifrada. La información cifrada con la clave pública solo se puede descifrar con la clave privada, de esta manera se garantiza que solo el destinatario podrá ver la informaicón, inclusive si la clave pública fue interceptada por personas mal intencionadas.

A partir de este principio nace la firma digital. Como se vio en el ejemplo anterior, solo el destinatario podía ver la información cifrada. De manera similar pero aplicando el algoritmo de manera inversa, solo en caso de que el signatario haya cifrado información con su llave privada, los demás podrán descifrar dicha información con la clave pública. De esta manera se garantiza que la información proviene del signatario, ya que la clave pública provista por él fue capaz de descifrar la información.

El certificado nace a partir de la infraestructura de clave pública con la necesidad de identificar al signatario sin intercambiar personalmente las claves públicas. Es por esto que el certificado es la conformación de la clave pública más los datos de identidad, de esta manera para saber a quien pertenece la clave pública, se puede revisar los datos de identidad.

Para generar el certificado se utiliza las siguientes instrucciones:

  1. const certificado = forge.pki.createCertificate();
  2. certificado.publicKey = keyPair.publicKey;
  3. certificado.validity.notBefore = new Date();
  4. certificado.validity.notAfter = new Date();
  5. certificado.validity.notAfter.setFullYear(certificado.validity.notBefore.getFullYear() + 1);

En el ejemplo anterior se crea un certificado, se le asigna la clave pública y el periodo de validez desde la fecha actual con una duración de un año.

Como se mencionó anteriormente se debe agregar los datos de identidad como se muestra en las siguientes líneas:

  1. const atributos = [{
  2. name: 'commonName',
  3. value: 'Juan Perez'
  4. }];
  5. certificado.setSubject(atributos);

Al ser un certificado autofirmado, los datos del emisor del certificado serían los mismos, por lo que se asignan los datos del signatario y se firma el certificado con las siguientes líneas:

  1. certificado.setIssuer(atributos);
  2. certificado.sign(keyPair.privateKey);

La firma se genear calculando un hash en base al algoritmo sha256, de manera que el documento se mantenga visible pero se detecte en caso de modificación:

  1. const md = forge.md.sha256.create();
  2. md.update('Cadena que se desea firmar.');
  3. const firma = keyPair.privateKey.sign(md);

En el ejemplo se puede observar el cálculo de un hash de tipo sha 256 sobre el documento (cadena) que se desea firmar y finalmente ese hash es encriptado con la llave privada obteniendo como resultado la firma de la cadena.

Un documento firmado se conforma por el contenido del documento, la firma y el certificado. Un ejemplo sería:

  1. const docFirmado = {};
  2. docFirmado.body = 'Cadena que se desea firmar.';
  3. docFirmado.firma = Buffer.from(firma, 'binary').toString('base64');
  4. docFirmado.certificado = forge.pki.certificateToPem(certificado);
  5. console.log(docFirmado);

Teniendo la firma, el certificado y la cadena, ya podemos realizar algunas pruebas, por ejemplo verificar que la firma corresponda a la cadena y al propietario del certificado.

  1. const md2 = forge.md.sha256.create();
  2. md2.update(docFirmado.body);
  3. console.log(forge.pki.certificateFromPem(docFirmado.certificado).publicKey.verify(md2.digest().bytes(), Buffer.from(docFirmado.firma, 'base64').toString('binary')));

Guardando el programa y haciéndolo correr debería devolver true, ya que la firma es válida.

  1. node app.js

Por el contrario si agregamos un código similar al anterior pero con una cadena diferente el resultado será false.

  1. const md3 = forge.md.sha256.create();
  2. md3.update('Cadena que se desea firmar');
  3. console.log(certificado.publicKey.verify(md3.digest().bytes(), firma));

Si desea descargar el ejemplo puede hacerlo a través del siguiente link: Código de ejemplo

© ADSIB 2019 Bolivia