UNPKG

facturacionelectronicapy-xmlsign

Version:

Módulo NodeJs para la firma de Archivo XML para la Facturación Electrónica en Paraguay

584 lines (557 loc) 37.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); //import convert from 'xml-js'; const xml2js_1 = __importDefault(require("xml2js")); //import sha256 from "crypto-js/sha256"; //import Base64 from "crypto-js/enc-base64"; const PKCS12_1 = __importDefault(require("./PKCS12")); const find_java_home_1 = __importDefault(require("find-java-home")); const XMLSign_1 = __importDefault(require("./XMLSign")); //import crypto from 'crypto'; //const { subtle } = require('crypto').webcrypto; const { exec } = require("child_process"); //var xmldom = require("xmldom"); var fs = require("fs"); //var c14n = require("xml-c14n")(); //var atob = require("atob"); //var xmldsigjs = require("xmldsigjs"); //var WebCrypto = require("node-webcrypto-ossl"); //xmldsigjs.Application.setEngine("OpenSSL", new WebCrypto()); //xmldsigjs.Application.setEngine("OpenSSL", new (await import("node-webcrypto-ossl")).Crypto) //var SignedXml = require('xml-crypto').SignedXml; //import { SignedXml, FileKeyInfo } from "xml-crypto"; class XMLDsig { constructor() { } openFile(file, passphase) { this.file = file; this.passphase = passphase; PKCS12_1.default.openFile(file, passphase); this.setXMLSignOpts(); } setXMLSignOpts() { /*this.signOpts = { compact: true, ignoreComment: true, spaces: 2, fullTagEmptyElement: true };*/ this.signedInfo = { CanonicalizationMethod: { $: { Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", }, }, SignatureMethod: { $: { Algorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", }, }, Reference: { $: { URI: "", }, Transforms: { Transform: [ { $: { Algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature", }, }, { $: { Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", }, }, ], }, DigestMethod: { $: { Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256", }, }, DigestValue: "", }, }; } /** * Firma con Java, retornando el documento firmado en el buffer de salida * @param xml * @param tag * @returns */ signDocument(xml, tag) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { //console.log("A firmar", xml); //xml = await this.asignarFechaFirma(xml); find_java_home_1.default({ allowJre: true }, (err, java8Path) => { java8Path += "/bin/java"; if (err) return console.log(err); //Comentar esta linea al llevar a Produccion, por que aqui trae java 6 if (process.env.java8_home) { //java8Path = `"C:\\Program Files\\Java\\jdk1.8.0_221\\bin\\java"`; java8Path = `${process.env.java8_home}`; } const classPath = "" + __dirname + ""; //const tmpXMLToSign = "" + __dirname + "/xml_sign_temp.xml"; const tmpXMLToSign = "" + __dirname + "/xml_sign_temp_" + Math.round(Math.random() * 999999) + ".xml"; fs.writeFileSync(tmpXMLToSign, xml, { encoding: "utf8" }); exec(`"${java8Path}" -Dfile.encoding=IBM850 -classpath "${classPath}" SignXML "${tmpXMLToSign}" "${this.file}" "${this.passphase}" "${tag}"`, { encoding: "UTF-8" }, (error, stdout, stderr) => { if (error) { reject(error); } if (stderr) { reject(stderr); } try { fs.unlinkSync(tmpXMLToSign); //file removed } catch (err) { console.error(err); } //console.log(`signedXML: ${stdout}`); //resolve(Buffer.from(`${stdout}`,'utf8').toString()); //fs.writeFileSync(tmpXMLToSign + ".result.xml", `${stdout}`, {encoding: 'utf8'}); //let resultXML = fs.readFileSync(tmpXMLToSign + ".result.xml", {encoding: 'utf8'}); resolve(`${stdout}`); }); }); })); }); } /** * Firma con Java, varios documentos al mismo tiempo * @param xml * @param tag * @returns */ signDocuments(xmls, tag) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { //console.log("A firmar", xml); find_java_home_1.default({ allowJre: true }, (err, java8Path) => { java8Path += "/bin/java"; if (err) return console.log(err); //Comentar esta linea al llevar a Produccion, por que aqui trae java 6 if (process.env.java8_home) { //java8Path = `"C:\\Program Files\\Java\\jdk1.8.0_221\\bin\\java"`; java8Path = `${process.env.java8_home}`; } const classPath = "" + __dirname + ""; //const tmpXMLToSign = "" + __dirname + "/xml_sign_temp.xml"; const arrayNameFiles = new Array(); for (let i = 0; i < xmls.length; i++) { const xml = xmls[i]; const tmpXMLToSign = "" + __dirname + "/xml_sign_temp_" + Math.round(Math.random() * 999999) + ".xml"; fs.writeFileSync(tmpXMLToSign, xml, { encoding: "utf8" }); arrayNameFiles.push(tmpXMLToSign); } exec(`"${java8Path}" -Dfile.encoding=IBM850 -classpath "${classPath}" SignXMLFiles "${arrayNameFiles.join(",")}" "${this.file}" "${this.passphase}" "${tag}"`, { encoding: "UTF-8" }, (error, stdout, stderr) => { let stdOutProcesed = stdout; /*console.log("error", error); console.log("stdout", stdout); console.log("stderr", stderr); */ if (stdOutProcesed.includes("_SEPARATOR_")) { //Por si haya algun log que quedo en la colita, entonces elimina ese stdOutProcesed = stdout.substring(0, stdout.lastIndexOf("_SEPARATOR_") + 11); } if (error) { if (!stdOutProcesed.includes("_SEPARATOR_")) { reject(error); } } if (stderr) { if (!stdOutProcesed.includes("_SEPARATOR_")) { reject(stderr); } } try { for (let i = 0; i < arrayNameFiles.length; i++) { const nameFile = arrayNameFiles[i]; fs.unlinkSync(nameFile); } //file removed } catch (err) { console.error(err); } //console.log(`signedXML: ${stdout}`); //resolve(Buffer.from(`${stdout}`,'utf8').toString()); //fs.writeFileSync(tmpXMLToSign + ".result.xml", `${stdout}`, {encoding: 'utf8'}); //let resultXML = fs.readFileSync(tmpXMLToSign + ".result.xml", {encoding: 'utf8'}); resolve(`${stdOutProcesed}`); }); }); })); }); } /** * Firma el XML del Evento con Java * @param xml * @param tag * @returns */ signEvento(xml, tag) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { //console.log("A firmar", xml); //xml = await this.asignarFechaFirma(xml); find_java_home_1.default({ allowJre: true }, (err, java8Path) => { java8Path += "/bin/java"; if (err) return console.log(err); //Comentar esta linea al llevar a Produccion, por que aqui trae java 6 if (process.env.java8_home) { //java8Path = `"C:\\Program Files\\Java\\jdk1.8.0_221\\bin\\java"`; java8Path = `${process.env.java8_home}`; } const classPath = "" + __dirname + ""; const tmpXMLToSign = "" + __dirname + "/xml_sign_temp.xml"; fs.writeFileSync(tmpXMLToSign, xml, { encoding: "utf8" }); exec(`"${java8Path}" -Dfile.encoding=IBM850 -classpath "${classPath}" SignXMLEvento "${tmpXMLToSign}" "${this.file}" "${this.passphase}"`, { encoding: "UTF-8" }, (error, stdout, stderr) => { if (error) { reject(error); } if (stderr) { reject(stderr); } try { fs.unlinkSync(tmpXMLToSign); //file removed } catch (err) { console.error(err); } //console.log(`signedXML: ${stdout}`); //resolve(Buffer.from(`${stdout}`,'utf8').toString()); //fs.writeFileSync(tmpXMLToSign + ".result.xml", `${stdout}`, {encoding: 'utf8'}); //let resultXML = fs.readFileSync(tmpXMLToSign + ".result.xml", {encoding: 'utf8'}); resolve(`${stdout}`); }); }); })); }); } /*async signDocument2(xml: string, tag: any) { xmldsigjs.Application.setEngine( "OpenSSL", new (await import("node-webcrypto-ossl")).Crypto() ); let id = ""; var parser = new xml2js.Parser({ explicitArray: false }); const xmlDocumentJSON: any = await parser.parseStringPromise(xml); if (tag) { if (!xmlDocumentJSON["rDE"][tag]) { throw new Error("Tag " + tag + " no encontrado en el Archivo XML"); } const idTag: any = xmlDocumentJSON["rDE"][tag].$.Id; id = `${idTag}`; } let key = Buffer.from(token.getPrivateKey() + "", "utf8"); let cert = Buffer.from(token.getCertificate() + "", "utf8"); let keysS = await this.importKey(token.getPrivateKey() + ""); let signature = new xmldsigjs.SignedXml(); signature .Sign({ name: "RSASSA-PKCS1-v1_5" }, keysS, xmldsigjs.Parse(xml), { references: [ { id, hash: "SHA-512", transforms: ["enveloped", "c14n"] }, ], }) .then(() => { console.log("sig.tostring...", signature.toString()); }); }*/ /*async signDocument3(xml: string, tag: any) { //var option = {implicitTransforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"]} var option = { implicitTransforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }; var sig = new SignedXml(null, option); sig.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; //sig.signatureAlgorithm = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; //sig.signatureAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; //sig.canonicalizationAlgorithm = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; sig.addReference( "//*[local-name(.)='DE']", [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], "http://www.w3.org/2001/04/xmlenc#sha256" ); //sig.addReference("//*[local-name(.)='DE']"); sig.signingKey = Buffer.from(token.getPrivateKey() + "", "utf8"); sig.keyInfoProvider = { file: "", getKeyInfo: (key, prefix) => { return `<X509Data><X509Certificate>${token.getCertificate()}</X509Certificate></X509Data>`; }, getKey: (keyInfo: Node): Buffer => { return Buffer.from(token.getPrivateKey() + ""); }, }; sig.computeSignature(xml); let xmlSigned = sig.getSignedXml(); token.clean(); return xmlSigned; }*/ /*async signDocument_El_que_que_funcionar(xml: string, tag: any) { var parser = new xml2js.Parser({ explicitArray: false }); const xmlDocumentJSON: any = await parser.parseStringPromise(xml); const $: any = { xmlns: "http://www.w3.org/2000/09/xmldsig#", }; if (tag) { if (!xmlDocumentJSON["rDE"][tag]) { throw new Error("Tag " + tag + " no encontrado en el Archivo XML"); } const idTag: any = xmlDocumentJSON["rDE"][tag].$.Id; this.signedInfo.Reference.$.URI = `#${idTag}`; } let objetoAFirmar = { ...xmlDocumentJSON["rDE"] }; delete objetoAFirmar.$; delete objetoAFirmar.dVerFor; var builder = new xml2js.Builder(); let xmlToDigest = builder.buildObject(objetoAFirmar); //console.log("xmlToDigest a", xmlToDigest); xmlToDigest = await this.normalizeXML(xmlToDigest); xmlToDigest = xmlToDigest.replace( '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', "" ); //xmlToDigest = await this.canonicalizar(xmlToDigest); //Prueba //xmlToDigest = xmlToDigest.replace('<DE Id="01800695631001001000000122020101411987164320">', ''); //xmlToDigest = xmlToDigest.replace('</DE>', ''); //xmlToDigest = xmlToDigest.split('\n').slice(1).join('\n'); const hash = this.digest(xmlToDigest); this.signedInfo.Reference.DigestValue = hash; var signedInfoXML = builder.buildObject({ SignedInfo: { $, CanonicalizationMethod: this.signedInfo.CanonicalizationMethod, SignatureMethod: this.signedInfo.SignatureMethod, Reference: this.signedInfo.Reference, }, }); //signedInfoXML = signedInfoXML.split('\n').slice(1).join('\n'); xmlToDigest = await this.canonicalizar(signedInfoXML); let signature = this.signatureValue(signedInfoXML); xmlDocumentJSON["rDE"].Signature = { $: { xmlns: "http://www.w3.org/2000/09/xmldsig#", }, SignedInfo: this.signedInfo, SignatureValue: signature, KeyInfo: { X509Data: { X509Certificate: this.certificate.split("\r").join(""), }, }, }; xmlDocumentJSON["rDE"]["gCamFuFD"] = this.agregarQr(xmlDocumentJSON); var xmlSigned = builder.buildObject(xmlDocumentJSON); //xmlSigned = await this.normalizeXML(xmlSigned); token.clean(); return xmlSigned; }*/ /* canonicalizar(xml: string): Promise<string> { return new Promise((resolve, reject) => { let document = new xmldom.DOMParser().parseFromString(xml); let canonicaliser = c14n.createCanonicaliser( "http://www.w3.org/2001/10/xml-exc-c14n#" ); canonicaliser.canonicalise( document.documentElement, function (err: any, res: any) { if (err) { reject(err); } resolve(res); } ); }); }*/ /*digest(xml: string) { var sha256 = crypto.createHash('sha256'); sha256.update(xml, 'utf8'); return sha256.digest('base64'); }*/ /* no usado digest(xml: string) { const hashDigest = sha256(xml); const hmacDigest = Base64.stringify(hashDigest); return hmacDigest; }*/ signatureValue(xml) { let privateKey = PKCS12_1.default.getPrivateKey(); this.certificate = PKCS12_1.default.getCertificate(); let signature = PKCS12_1.default.signature(xml, privateKey); //console.log("signature", signature); return Buffer.from(signature, "binary").toString("base64"); } getCertificate() { let certificate = PKCS12_1.default.getCertificate(); return `-----BEGIN CERTIFICATE-----${certificate}\n-----END CERTIFICATE-----`; } normalizeXML(xml) { xml = xml.split("\r\n").join(""); xml = xml.split("\n").join(""); xml = xml.split("\t").join(""); xml = xml.split(" ").join(""); xml = xml.split("> <").join("><"); xml = xml.split("> <").join("><"); xml = xml.replace(/\r?\n|\r/g, ""); return xml; } agregarQr(obj) { let qrResult = {}; qrResult["dCarQR"] = { _: "reemplazarQrCode", }; return qrResult; } removeLines(str) { return str.replace("\n", ""); } base64ToArrayBuffer(b64) { var byteString = atob(b64); var byteArray = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { byteArray[i] = byteString.charCodeAt(i); } return byteArray; } pemToArrayBuffer(pem) { var b64Lines = this.removeLines(pem); b64Lines = b64Lines.replace("-----BEGIN PRIVATE KEY-----", ""); b64Lines = b64Lines.replace("-----END PRIVATE KEY-----", ""); b64Lines = b64Lines.replace("-----BEGIN RSA PRIVATE KEY-----", ""); b64Lines = b64Lines.replace("-----END RSA PRIVATE KEY-----", ""); return this.base64ToArrayBuffer(b64Lines); } /* async importKey(yourprivatekey: string) { let crypto = new (await import("node-webcrypto-ossl")).Crypto(); //window.crypto.subtle.importKey( return crypto.subtle.importKey( "pkcs8", this.pemToArrayBuffer(yourprivatekey), { name: "RSA-OAEP", hash: { name: "SHA-256" }, // or SHA-512 }, true, ["decrypt"] ); }*/ asignarFechaFirma(xml) { return __awaiter(this, void 0, void 0, function* () { var parser = new xml2js_1.default.Parser({ explicitArray: false }); const xmlDocumentJSON = yield parser.parseStringPromise(xml); xmlDocumentJSON["rDE"]["DE"]["dFecFirma"] = this.jsonFormat(new Date()); var builder = new xml2js_1.default.Builder(); xml = builder.buildObject(xmlDocumentJSON); xml = this.normalizeXML(xml); return xml; }); } jsonFormat(fecha) { return (fecha.getFullYear() + "-" + this.lpad(fecha.getMonth() + 1, 2) + "-" + this.lpad(fecha.getDate(), 2) + "T" + this.lpad(fecha.getHours(), 2) + ":" + this.lpad(fecha.getMinutes(), 2) + ":" + this.lpad(fecha.getSeconds(), 2)); } lpad(num, size) { let s = num + ""; while (s.length < size) s = "0" + s; return s; } /** * Firma con Java, retornando el documento firmado en el buffer de salida * @param xml * @param tag * @returns */ getExpiration() { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { //console.log("A firmar", xml); //xml = await this.asignarFechaFirma(xml); find_java_home_1.default({ allowJre: true }, (err, java8Path) => { java8Path += "/bin/java"; if (err) return console.log(err); //Comentar esta linea al llevar a Produccion, por que aqui trae java 6 if (process.env.java8_home) { //java8Path = `"C:\\Program Files\\Java\\jdk1.8.0_221\\bin\\java"`; java8Path = `${process.env.java8_home}`; } const classPath = "" + __dirname + ""; exec(`"${java8Path}" -Dfile.encoding=IBM850 -classpath "${classPath}" CertExpiration "${this.file}" "${this.passphase}"`, { encoding: "UTF-8" }, (error, stdout, stderr) => { if (error) { reject(error); } if (stderr) { reject(stderr); } try { //file removed } catch (err) { console.error(err); } resolve(`${stdout}`); }); }); })); }); } /** * Firma con NODEJS, varios documentos al mismo tiempo * @param xml * @param tag * @returns * https://tutoriales.adsib.gob.bo/_08/index.html */ signDocumentsWithNodeJS(xmls, tag, file, password) { return __awaiter(this, void 0, void 0, function* () { return XMLSign_1.default.signWithNode(xmls, tag, file, password); }); } } exports.default = new XMLDsig(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWE1MRHNpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9YTUxEc2lnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7O0FBQUEsK0JBQStCO0FBQy9CLG9EQUE0QjtBQUU1Qix3Q0FBd0M7QUFDeEMsNENBQTRDO0FBQzVDLHNEQUE2QjtBQUM3QixvRUFBMEM7QUFDMUMsd0RBQWdDO0FBR2hDLDhCQUE4QjtBQUM5QixpREFBaUQ7QUFDakQsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUMxQyxpQ0FBaUM7QUFDakMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3ZCLG1DQUFtQztBQUVuQyw2QkFBNkI7QUFFN0IsdUNBQXVDO0FBQ3ZDLGlEQUFpRDtBQUNqRCw4REFBOEQ7QUFDOUQsOEZBQThGO0FBQzlGLGtEQUFrRDtBQUNsRCxzREFBc0Q7QUFFdEQsTUFBTSxPQUFPO0lBT1gsZ0JBQWUsQ0FBQztJQUVoQixRQUFRLENBQUMsSUFBWSxFQUFFLFNBQWlCO1FBQ3RDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO1FBQzNCLGdCQUFLLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVELGNBQWM7UUFDWjs7Ozs7Z0JBS1E7UUFDUixJQUFJLENBQUMsVUFBVSxHQUFHO1lBQ2hCLHNCQUFzQixFQUFFO2dCQUN0QixDQUFDLEVBQUU7b0JBQ0QsU0FBUyxFQUFFLHlDQUF5QztpQkFDckQ7YUFDRjtZQUNELGVBQWUsRUFBRTtnQkFDZixDQUFDLEVBQUU7b0JBQ0QsU0FBUyxFQUFFLG1EQUFtRDtpQkFDL0Q7YUFDRjtZQUNELFNBQVMsRUFBRTtnQkFDVCxDQUFDLEVBQUU7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