UNPKG

envio-comprobantes-sri

Version:

Envia comprobantes electronicos al SRI (Ecuador). Recibe un objeto JSON. Lo convierte a XML, lo firma, lo envía al servicio web del SRI y devuelve la respuesta.

199 lines (198 loc) 11.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FirmarComprobanteXML = void 0; const dom_parser_1 = __importDefault(require("dom-parser")); const node_forge_1 = __importDefault(require("node-forge")); const moment_1 = __importDefault(require("moment")); const Crear_hash_1 = __importDefault(require("./Utils/Crear-hash")); const Crear_hash_2 = __importDefault(require("./Utils/Crear-hash")); function sha1_base64(txt, encoding) { var md = node_forge_1.default.md.sha1.create(); md.update(txt, encoding); return Buffer.from(md.digest().toHex(), 'hex').toString('base64'); } function p_generar_xades_bes(p12cert, comprobante, callback) { var parser = new dom_parser_1.default(); var xmlDoc = parser.parseFromString(comprobante); var claveAcceso = xmlDoc.getElementsByTagName("claveAcceso")[0].textContent; const { modulus, certificateX509, certificateX509_der_hash, X509SerialNumber, exponent, issuerName } = generarFirma(p12cert, comprobante); /** Especifico la codificación del xml (utf8) */ var sha1_comprobante = sha1_base64(comprobante.replace('<?xml version="1.0" encoding="UTF-8"?>\n', ''), "utf8"); var xmlns = 'xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:etsi="http://uri.etsi.org/01903/v1.3.2#"'; //numeros involucrados en los hash: var Certificate_number = p_obtener_aleatorio(); //1562780 en el ejemplo del SRI var Signature_number = p_obtener_aleatorio(); //620397 en el ejemplo del SRI var SignedProperties_number = p_obtener_aleatorio(); //24123 en el ejemplo del SRI //numeros fuera de los hash: var SignedInfo_number = p_obtener_aleatorio(); //814463 en el ejemplo del SRI var SignedPropertiesID_number = p_obtener_aleatorio(); //157683 en el ejemplo del SRI var Reference_ID_number = p_obtener_aleatorio(); //363558 en el ejemplo del SRI var SignatureValue_number = p_obtener_aleatorio(); //398963 en el ejemplo del SRI var Object_number = p_obtener_aleatorio(); //231987 en el ejemplo del SRI var SignedProperties = ''; SignedProperties += '<etsi:SignedProperties Id="Signature' + Signature_number + '-SignedProperties' + SignedProperties_number + '">'; //SignedProperties SignedProperties += '<etsi:SignedSignatureProperties>'; SignedProperties += '<etsi:SigningTime>'; SignedProperties += (0, moment_1.default)().format('YYYY-MM-DD\THH:mm:ssZ'); SignedProperties += '</etsi:SigningTime>'; SignedProperties += '<etsi:SigningCertificate>'; SignedProperties += '<etsi:Cert>'; SignedProperties += '<etsi:CertDigest>'; SignedProperties += '<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1">'; SignedProperties += '</ds:DigestMethod>'; SignedProperties += '<ds:DigestValue>'; SignedProperties += certificateX509_der_hash; SignedProperties += '</ds:DigestValue>'; SignedProperties += '</etsi:CertDigest>'; SignedProperties += '<etsi:IssuerSerial>'; SignedProperties += '<ds:X509IssuerName>'; SignedProperties += issuerName; SignedProperties += '</ds:X509IssuerName>'; SignedProperties += '<ds:X509SerialNumber>'; SignedProperties += X509SerialNumber; SignedProperties += '</ds:X509SerialNumber>'; SignedProperties += '</etsi:IssuerSerial>'; SignedProperties += '</etsi:Cert>'; SignedProperties += '</etsi:SigningCertificate>'; SignedProperties += '</etsi:SignedSignatureProperties>'; SignedProperties += '<etsi:SignedDataObjectProperties>'; SignedProperties += '<etsi:DataObjectFormat ObjectReference="#Reference-ID-' + Reference_ID_number + '">'; SignedProperties += '<etsi:Description>'; SignedProperties += 'contenido comprobante'; SignedProperties += '</etsi:Description>'; SignedProperties += '<etsi:MimeType>'; SignedProperties += 'text/xml'; SignedProperties += '</etsi:MimeType>'; SignedProperties += '</etsi:DataObjectFormat>'; SignedProperties += '</etsi:SignedDataObjectProperties>'; SignedProperties += '</etsi:SignedProperties>'; //fin SignedProperties var SignedProperties_para_hash = SignedProperties.replace('<etsi:SignedProperties', '<etsi:SignedProperties ' + xmlns); var sha1_SignedProperties = sha1_base64(SignedProperties_para_hash); var KeyInfo = '<ds:KeyInfo Id="Certificate' + Certificate_number + '">'; KeyInfo += '\n<ds:X509Data>'; KeyInfo += '\n<ds:X509Certificate>\n'; KeyInfo += certificateX509; KeyInfo += '\n</ds:X509Certificate>'; KeyInfo += '\n</ds:X509Data>'; KeyInfo += '\n<ds:KeyValue>'; KeyInfo += '\n<ds:RSAKeyValue>'; KeyInfo += '\n<ds:Modulus>\n'; //MODULO DEL CERTIFICADO X509 KeyInfo += modulus; KeyInfo += '\n</ds:Modulus>'; KeyInfo += '\n<ds:Exponent>'; //KeyInfo += 'AQAB'; KeyInfo += exponent; KeyInfo += '</ds:Exponent>'; KeyInfo += '\n</ds:RSAKeyValue>'; KeyInfo += '\n</ds:KeyValue>'; KeyInfo += '\n</ds:KeyInfo>'; var KeyInfo_para_hash = KeyInfo.replace('<ds:KeyInfo', '<ds:KeyInfo ' + xmlns); var sha1_certificado = sha1_base64(KeyInfo_para_hash); var SignedInfo = '<ds:SignedInfo Id="Signature-SignedInfo' + SignedInfo_number + '">'; SignedInfo += '\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">'; SignedInfo += '</ds:CanonicalizationMethod>'; SignedInfo += '\n<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1">'; SignedInfo += '</ds:SignatureMethod>'; SignedInfo += '\n<ds:Reference Id="SignedPropertiesID' + SignedPropertiesID_number + '" Type="http://uri.etsi.org/01903#SignedProperties" URI="#Signature' + Signature_number + '-SignedProperties' + SignedProperties_number + '">'; SignedInfo += '\n<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1">'; SignedInfo += '</ds:DigestMethod>'; SignedInfo += '\n<ds:DigestValue>'; //HASH O DIGEST DEL ELEMENTO <etsi:SignedProperties>'; SignedInfo += sha1_SignedProperties; SignedInfo += '</ds:DigestValue>'; SignedInfo += '\n</ds:Reference>'; SignedInfo += '\n<ds:Reference URI="#Certificate' + Certificate_number + '">'; SignedInfo += '\n<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1">'; SignedInfo += '</ds:DigestMethod>'; SignedInfo += '\n<ds:DigestValue>'; //HASH O DIGEST DEL CERTIFICADO X509 SignedInfo += sha1_certificado; SignedInfo += '</ds:DigestValue>'; SignedInfo += '\n</ds:Reference>'; SignedInfo += '\n<ds:Reference Id="Reference-ID-' + Reference_ID_number + '" URI="#comprobante">'; SignedInfo += '\n<ds:Transforms>'; SignedInfo += '\n<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature">'; SignedInfo += '</ds:Transform>'; SignedInfo += '\n</ds:Transforms>'; SignedInfo += '\n<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1">'; SignedInfo += '</ds:DigestMethod>'; SignedInfo += '\n<ds:DigestValue>'; //HASH O DIGEST DE TODO EL ARCHIVO XML IDENTIFICADO POR EL id="comprobante" SignedInfo += sha1_comprobante; SignedInfo += '</ds:DigestValue>'; SignedInfo += '\n</ds:Reference>'; SignedInfo += '\n</ds:SignedInfo>'; var SignedInfo_para_firma = SignedInfo.replace('<ds:SignedInfo', '<ds:SignedInfo ' + xmlns); const firma_SignedInfo = (0, Crear_hash_1.default)(p12cert, SignedInfo_para_firma); var xades_bes = ''; //INICIO DE LA FIRMA DIGITAL xades_bes += '<ds:Signature ' + xmlns + ' Id="Signature' + Signature_number + '">'; xades_bes += '\n' + SignedInfo; xades_bes += '\n<ds:SignatureValue Id="SignatureValue' + SignatureValue_number + '">\n'; //VALOR DE LA FIRMA (ENCRIPTADO CON LA LLAVE PRIVADA DEL CERTIFICADO DIGITAL) xades_bes += firma_SignedInfo; xades_bes += '\n</ds:SignatureValue>'; xades_bes += '\n' + KeyInfo; xades_bes += '\n<ds:Object Id="Signature' + Signature_number + '-Object' + Object_number + '">'; xades_bes += '<etsi:QualifyingProperties Target="#Signature' + Signature_number + '">'; //ELEMENTO <etsi:SignedProperties>'; xades_bes += SignedProperties; xades_bes += '</etsi:QualifyingProperties>'; xades_bes += '</ds:Object>'; xades_bes += '</ds:Signature>'; //FIN DE LA FIRMA DIGITAL callback(comprobante.replace(/<\/\w+>$/, closing_tag => xades_bes + closing_tag), claveAcceso); } function generarFirma(p12, infoAFirmar) { var certificateX509 = ''; var certBags = p12.getBags({ bagType: node_forge_1.default.pki.oids.certBag }); var cert = certBags[node_forge_1.default.pki.oids.certBag][0].cert; var pkcs8bags = p12.getBags({ bagType: node_forge_1.default.pki.oids.pkcs8ShroudedKeyBag }); var pkcs8 = pkcs8bags[node_forge_1.default.pki.oids.pkcs8ShroudedKeyBag][0]; var key = pkcs8.key; var signature = (0, Crear_hash_2.default)(p12, infoAFirmar); var certificateX509_pem = node_forge_1.default.pki.certificateToPem(cert); certificateX509 = certificateX509_pem; certificateX509 = certificateX509.substr(certificateX509.indexOf('\n')); certificateX509 = certificateX509.substr(0, certificateX509.indexOf('\n-----END CERTIFICATE-----')); certificateX509 = certificateX509.replace(/\r?\n|\r/g, '').replace(/([^\0]{76})/g, '$1\n'); //Pasar certificado a formato DER y sacar su hash: var certificateX509_asn1 = node_forge_1.default.pki.certificateToAsn1(cert); var certificateX509_der = node_forge_1.default.asn1.toDer(certificateX509_asn1).getBytes(); var certificateX509_der_hash = sha1_base64(certificateX509_der); //Serial Number var X509SerialNumber = BigInt("0x" + cert.serialNumber).toString(10); var exponent = hexToBase64(key.e.data[0].toString(16)); var modulus = bigint2base64(key.n); var modulus_pem = modulus; var issuerName = cert.issuer.attributes.map(attr => `${attr.shortName || attr.type}=${attr.value}`).reverse().join(', '); return { signature, certificateX509, modulus, certificateX509_pem, modulus_pem, certificateX509_der_hash, X509SerialNumber, exponent, issuerName }; } function bigint2base64(bigint) { var base64 = Buffer.from(bigint.toString(16).match(/\w{2}/g)?.map(function (a) { return String.fromCharCode(parseInt(a, 16)); }).join("")).toString("base64"); return base64.match(/.{1,76}/g)?.join("\n"); } function p_obtener_aleatorio() { return Math.floor(Math.random() * 999000) + 990; } function hexToBase64(str) { var hex = ('00' + str).slice(0 - str.length - str.length % 2); return Buffer.from(String.fromCharCode.apply(null, hex.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))).toString("base64"); } function FirmarComprobanteXML(p12cert, comprobante_xml) { return new Promise((resolve, reject) => { p_generar_xades_bes(p12cert, comprobante_xml, function (comprobante_firmado, claveAcceso) { resolve({ comprobante_firmado, claveAcceso }); }); }); } exports.FirmarComprobanteXML = FirmarComprobanteXML;