Firma digital de XML con token criptográfico

En este tutorial, se verá la implementación de firma digital desde una aplicación escrita en NodeJS, haciendo uso de un dispositivo criptográfico con el certificado contenido en el token.

Para la firma y acceso al Token criptográfico se utilizará la librería pkcs11-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 pkcs11-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('pkcs11-xml');
  2. var dsig = new Dsig('/usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so');

Con el código anterior, se importa la librería pkcs11-xml y se carga la interfaz de acceso al dispositivo criptográfico, en este caso opensc.

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 dispositivos criptográficos, ofrecen la posibilibdad de almacenar más de un par de claves, por lo que es necesario listar su contenido antes de poder firmar un documento. Para esto escribimos lo siguiente a continuación del programa iniciado en el paso anterior.

  1. try {
  2. dsig.openSession('12345678');
  3. const claves = dsig.getPrivateKeys();
  4. for (let i = 0; i < claves.length; i++) {
  5. console.log(claves[i].label);
  6. }
  7. } catch(e) {
  8. console.error(e);
  9. } finally {
  10. dsig.closeSession();
  11. }

En el ejemplo anterior se inicia sessión en el token a través del pin de seguridad y en caso de éxito se ejecutará la siguiente línea listando las claves privadas almacenadas en el token. 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 primera clave encontrada. El siguiente código debe ser agregado a continuación de la llave que finaliza el ciclo for.

  1. dsig.privateKey = claves[0].label;
  2. var xml = '<libro><titulo>Viaje al centro de la tierra</titulo><autor>Julio Berne</autor></libro>';
  3. 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