Firma digital de XML con certificado por software

En este tutorial, se verá la implementación de firma digital desde una aplicación escrita en NodeJS, haciendo uso de un par de claves y el certificado disponible en un contenedor PKCS#12.

Para la firma y acceso al Token criptográfico se utilizará la librería pkcs12-xml ya que es muy fácil de utilizar lo que viene bien para este propósito. El estándar utilizado para la firma digital del XML es DSIG que además coincide con la firma utilizada en los sistemas de facturación electrónica.

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-xml
  2. cd firmar-xml
  3. npm init
  4. npm install pkcs12-xml
  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 código:

  1. const Dsig = require('pkcs12-xml');
  2. var dsig = new Dsig('store.p12');

Con el código anterior, se importa la librería pkcs12-xml y se carga la interfaz de acceso al dispositivo criptográfico por software PKCS#12 cargando el par de claves desde el archivo store.p12. Para poder generar el par de claves con un certificado autofirmado seguir los siguientes pasos:

  • Descargar e instalar el generador de par de claves por software de la siguiente URL: Certificado por Software
  • Crear un nuevo par de claves con el menú "Archivo->Nuevo".
  • Dentro de la carpeta del proyecto guardar el par de claves con el nombre store.p12
  • Llenar los datos del formulario y pulsar el botón Generar CSR.
  • Introducir la contraseña dos veces y el nombre de la solicitud CSR para el certificado oficial.

El estándar XMLDSIG permite firmar documentos XML de diferentes formas, sin embargo para este tutorial se utilizará específicamente la siguiente:

  • Canonicalization.- http://www.w3.org/TR/2001/REC-xml-c14n-20010315
  • Transformation Algorithm.- http://www.w3.org/2000/09/xmldsig#enveloped-signature
  • Hashing Algorithm.- http://www.w3.org/2001/04/xmlenc#sha256
  • Signature Algorithm.- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256

Los contenedores PKCS#12 permiten almacenar un par de claves, protegido mediante contraseña. Para iniciar sesión, se debe especificar la contraseña de seguridad.

  1. try {
  2. dsig.openSession('12345678');
  3. } catch(e) {
  4. console.error(e);
  5. } finally {
  6. dsig.closeSession();
  7. }

En el ejemplo anterior se inicia sessión accediendo a la clave privada a través del pin de seguridad y en caso de éxito continua cerrando sesión. En caso de error se muestra el mensaje y finalmente se cierra la sesión.

A continuación se describe como firmar el tag "titulo" de un documento XML con la clave privada contenida en el archivo PKCS#12. El siguiente código debe ser agregado a continuación de la línea que inicia sesión.

  1. var xml = '<libro><titulo>Viaje al centro de la tierra</titulo><autor>Julio Berne</autor></libro>';
  2. console.log(dsig.computeSignature(xml, 'titulo'));

El resultado final después de la firma, es el XML de la variable xml, agregando dentro del tag libro la firma del documento como se muestra a continuación:

  1. <libro>
  2. <titulo id="titulo">Viaje al centro de la tierra</titulo>
  3. <autor>Julio Berne</autor>
  4. <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
  5. <SignedInfo>
  6. <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
  7. <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
  8. <Reference URI="#titulo">
  9. <Transforms>
  10. <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
  11. <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></Transform>
  12. </Transforms>
  13. <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
  14. <DigestValue>N1dKcogNSj4gE1lxjNKWpFxmTpKpZ1Uilj6wIt7yxGQ=</DigestValue>
  15. </Reference>
  16. </SignedInfo>
  17. <SignatureValue>mdLAY/sdrRDbO9K7vYywLN660KysgS2PvepuN6xrSPZ0g+0hoOY6ANqPwqn7AV9oeeFHHaNi7l4j6sfZQBqbVSUtffCXCMbgnHgwk3dPZyej558HCt5CIrPmzi02jKqM+GL2XAXsX1kP2goU6XbWcBTVIGZM9e+wP9n5wyYCqgiUDkESAWRO9jbo2P7MbvILNBnH862ivshw4IM9T/+xxgINPtN3OLMKwJ34kJvRVrBR+Yod3CfsTzUes0bT1+q4CejWcu5ogXD/17n2rWRuF8nsSO1jYOLPnxInJVSfW7RePlWeJkE6SjcV11vuCxvc7/d40Ja9YVk5Q9VYCQfMHg==</SignatureValue>
  18. <KeyInfo>
  19. <X509Data>
  20. <X509Certificate>MIID3DCCAsSgAwIBAgIJAMUWAJ4eqSiyMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
  21. VQQGEwJCTzEPMA0GA1UECAwGTGEgUGF6MQ8wDQYDVQQHDAZMYSBQYXoxDjAMBgNV
  22. BAoMBUFEU0lCMQwwCgYDVQQLDANVSUQxEzARBgNVBAMMCkp1YW4gUGVyZXoxHjAc
  23. BgkqhkiG9w0BCQEWD2pwZXJlekBtYWlsLmNvbTAeFw0xOTA2MjcxOTAxNTJaFw0x
  24. OTA3MjcxOTAxNTJaMIGCMQswCQYDVQQGEwJCTzEPMA0GA1UECAwGTGEgUGF6MQ8w
  25. DQYDVQQHDAZMYSBQYXoxDjAMBgNVBAoMBUFEU0lCMQwwCgYDVQQLDANVSUQxEzAR
  26. BgNVBAMMCkp1YW4gUGVyZXoxHjAcBgkqhkiG9w0BCQEWD2pwZXJlekBtYWlsLmNv
  27. bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6Fg+eCOnsHxH4ls9RQ
  28. 9CGD/q6On6e0XKEzXxdM3YBFnQGiVXQ+odeJEKzTx6QKUP+gYyXLRzZTiJO9DNBM
  29. 5J90LRpvo34g6qIFxAEjuK0TP44bpPw8KVspk+OGaG2i44mzdKL5Hrh1PV+P87Dg
  30. JppceU2JmW73M+WRWUMDPk8XSxfelNvGHR8vWq2vxqOMd6blu9rIYBDEC10r48sn
  31. tjF/bhnHmQn9volMM3Dj0hH58kyEwt2bSgt+rBvIFuFMyDmSshA1tWhC1ITyjFgp
  32. 6w+tu90FONV3Rf4cn3TNgygD5GE/+z34ZXybX6qnd/JbPwqMGZ7K+SqHz6rdx/64
  33. b/UCAwEAAaNTMFEwHQYDVR0OBBYEFGuAkfgego+nx3fxrFnZ2qbREC2jMB8GA1Ud
  34. IwQYMBaAFGuAkfgego+nx3fxrFnZ2qbREC2jMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
  35. KoZIhvcNAQELBQADggEBAHKJNitx/h059KY9Asslqq09/fp27EusWG66eKBAeBR0
  36. qtxwSQ7yAXzpmSbzHFAP3j0iQLYeZ8HNhfUbsBoNoLPULwvfEkvpC24DCzfVGoJD
  37. LgtewQpWibYwh/279OYcaKRPMq5hJjG2OdSXlTBY1vEp24AvRcyBC5/h1wHfjyTA
  38. kO1cminOqIWC77ButB+rbTOObSL4SbuwP7Es1XCe0TXpklbSQIiWMpLjTh87JAS2
  39. Y3REEdw0e7fZtaQ5WvUeo3C2eksNfZSzF5Ch3WqKioD7KtiwCwxeKJpbTb+O4ZYU
  40. mxPwYiubMcA4fpPz0g+Q79+Bbnp9/6ZIYGmVJ6GFNw8=</X509Certificate>
  41. </X509Data>
  42. </KeyInfo>
  43. </Signature></libro>

Se puede observar que dentro del tag Signature que se agregó al documento, se encuentran las especifiaciones de configuraicón para la firma XML-DSIG para que la firma pueda ser validada con solo el documento. Así mismo se agrega el certificado del signatario para la correspondiente validación.

Para ejecutar el programa simplemente utilizar:

  1. node app.js

Para guardar el resultado en un archivo agregar en la cabecera:

  1. const fs = require('fs');

Y después del console.log o en lugar de:

  1. fs.writeFileSync('/tmp/xml.xml', dsig.computeSignature(xml, 'titulo'));

Si desea descargar un ejemplo de archivo firmado puede hacerlo a través del siguiente link: Archivo firmado de ejemplo

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

© ADSIB 2019 Bolivia