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:
- mkdir firmar-xml
- cd firmar-xml
- npm init
- npm install pkcs12-xml
- 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:
- const Dsig = require('pkcs12-xml');
- 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.
- try {
- dsig.openSession('12345678');
- } catch(e) {
- console.error(e);
- } finally {
- dsig.closeSession();
- }
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.
- var xml = '<libro><titulo>Viaje al centro de la tierra</titulo><autor>Julio Berne</autor></libro>';
- 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:
- <libro>
- <titulo id="titulo">Viaje al centro de la tierra</titulo>
- <autor>Julio Berne</autor>
- <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
- <SignedInfo>
- <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
- <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
- <Reference URI="#titulo">
- <Transforms>
- <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
- <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></Transform>
- </Transforms>
- <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
- <DigestValue>N1dKcogNSj4gE1lxjNKWpFxmTpKpZ1Uilj6wIt7yxGQ=</DigestValue>
- </Reference>
- </SignedInfo>
- <SignatureValue>mdLAY/sdrRDbO9K7vYywLN660KysgS2PvepuN6xrSPZ0g+0hoOY6ANqPwqn7AV9oeeFHHaNi7l4j6sfZQBqbVSUtffCXCMbgnHgwk3dPZyej558HCt5CIrPmzi02jKqM+GL2XAXsX1kP2goU6XbWcBTVIGZM9e+wP9n5wyYCqgiUDkESAWRO9jbo2P7MbvILNBnH862ivshw4IM9T/+xxgINPtN3OLMKwJ34kJvRVrBR+Yod3CfsTzUes0bT1+q4CejWcu5ogXD/17n2rWRuF8nsSO1jYOLPnxInJVSfW7RePlWeJkE6SjcV11vuCxvc7/d40Ja9YVk5Q9VYCQfMHg==</SignatureValue>
- <KeyInfo>
- <X509Data>
- <X509Certificate>MIID3DCCAsSgAwIBAgIJAMUWAJ4eqSiyMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
- VQQGEwJCTzEPMA0GA1UECAwGTGEgUGF6MQ8wDQYDVQQHDAZMYSBQYXoxDjAMBgNV
- BAoMBUFEU0lCMQwwCgYDVQQLDANVSUQxEzARBgNVBAMMCkp1YW4gUGVyZXoxHjAc
- BgkqhkiG9w0BCQEWD2pwZXJlekBtYWlsLmNvbTAeFw0xOTA2MjcxOTAxNTJaFw0x
- OTA3MjcxOTAxNTJaMIGCMQswCQYDVQQGEwJCTzEPMA0GA1UECAwGTGEgUGF6MQ8w
- DQYDVQQHDAZMYSBQYXoxDjAMBgNVBAoMBUFEU0lCMQwwCgYDVQQLDANVSUQxEzAR
- BgNVBAMMCkp1YW4gUGVyZXoxHjAcBgkqhkiG9w0BCQEWD2pwZXJlekBtYWlsLmNv
- bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6Fg+eCOnsHxH4ls9RQ
- 9CGD/q6On6e0XKEzXxdM3YBFnQGiVXQ+odeJEKzTx6QKUP+gYyXLRzZTiJO9DNBM
- 5J90LRpvo34g6qIFxAEjuK0TP44bpPw8KVspk+OGaG2i44mzdKL5Hrh1PV+P87Dg
- JppceU2JmW73M+WRWUMDPk8XSxfelNvGHR8vWq2vxqOMd6blu9rIYBDEC10r48sn
- tjF/bhnHmQn9volMM3Dj0hH58kyEwt2bSgt+rBvIFuFMyDmSshA1tWhC1ITyjFgp
- 6w+tu90FONV3Rf4cn3TNgygD5GE/+z34ZXybX6qnd/JbPwqMGZ7K+SqHz6rdx/64
- b/UCAwEAAaNTMFEwHQYDVR0OBBYEFGuAkfgego+nx3fxrFnZ2qbREC2jMB8GA1Ud
- IwQYMBaAFGuAkfgego+nx3fxrFnZ2qbREC2jMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
- KoZIhvcNAQELBQADggEBAHKJNitx/h059KY9Asslqq09/fp27EusWG66eKBAeBR0
- qtxwSQ7yAXzpmSbzHFAP3j0iQLYeZ8HNhfUbsBoNoLPULwvfEkvpC24DCzfVGoJD
- LgtewQpWibYwh/279OYcaKRPMq5hJjG2OdSXlTBY1vEp24AvRcyBC5/h1wHfjyTA
- kO1cminOqIWC77ButB+rbTOObSL4SbuwP7Es1XCe0TXpklbSQIiWMpLjTh87JAS2
- Y3REEdw0e7fZtaQ5WvUeo3C2eksNfZSzF5Ch3WqKioD7KtiwCwxeKJpbTb+O4ZYU
- mxPwYiubMcA4fpPz0g+Q79+Bbnp9/6ZIYGmVJ6GFNw8=</X509Certificate>
- </X509Data>
- </KeyInfo>
- </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:
- node app.js
Para guardar el resultado en un archivo agregar en la cabecera:
- const fs = require('fs');
Y después del console.log o en lugar de:
- 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