UNPKG

facturacionelectronicapy-xmlsign

Version:

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

259 lines 23.4 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 }); //const pkcs12 = require('facturacionelectronicapy-pkcs12'); const fs = require("fs"); const { SignedXml, FileKeyInfo } = require("xml-crypto"); const xmlbuilder = require("xmlbuilder"); const xml2js = require("xml2js"); const node_forge_1 = __importDefault(require("node-forge")); class XMLDsigNode { signDocuments(xmls, tag, file, password) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var dsig = null; try { var separator = "_SEPARATOR_"; this.openFile(file, password); let certificate = this.getCertificate(); // Crear un objeto SignedXml // Configurar la clave privada para firmar (ejemplo, deberías cargar tu propia clave privada) let xmlFirmado = ""; for (let i = 0; i < xmls.length; i++) { const xmlString = xmls[i]; const sig = new SignedXml({ publicKey: this.getCertificate(), privateKey: this.getPrivateKey(), passphrase: password, getKeyInfoContent: (publicKey, prefix) => { const certContent = certificate.replace(/(?:\r\n|\r|\n)/g, ""); // Remover saltos de línea del certificado return `<X509Data><X509Certificate>${certContent}</X509Certificate></X509Data>`; }, }); const jsonXML = yield xml2js.parseStringPromise(xmlString); const idAtributo = jsonXML.rDE[tag][0].$.Id; sig.addReference( /*"#" + idAtributo, */ { xpath: "//*[local-name()='" + tag + "']", digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; // Algoritmo de firma RSA con SHA-256 sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; // Calcular la firma sig.computeSignature(xmlString); // Obtener la firma en formato XML const xmlWithSignature = sig.getSignedXml(); xmlFirmado += xmlWithSignature + separator; } //Retira el ultimo _SEPARATOR_ xmlFirmado = xmlFirmado.substring(0, xmlFirmado.length - separator.length); resolve(xmlFirmado); } catch (e) { console.error(e); reject(e); } finally { if (dsig != null) { //dsig.closeSession(); } } })); }); } signDocument(xmlString, tag, file, password) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var dsig = null; try { this.openFile(file, password); let certificate = this.getCertificate(); // Crear un objeto SignedXml // Configurar la clave privada para firmar (ejemplo, deberías cargar tu propia clave privada) let xmlFirmado = ""; const sig = new SignedXml({ publicKey: this.getCertificate(), privateKey: this.getPrivateKey(), passphrase: password, getKeyInfoContent: (publicKey, prefix) => { const certContent = certificate.replace(/(?:\r\n|\r|\n)/g, ""); // Remover saltos de línea del certificado return `<X509Data><X509Certificate>${certContent}</X509Certificate></X509Data>`; }, }); const jsonXML = yield xml2js.parseStringPromise(xmlString); const idAtributo = jsonXML.rDE[tag][0].$.Id; sig.addReference( /*"#" + idAtributo, */ { xpath: "//*[local-name()='" + tag + "']", digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; // Algoritmo de firma RSA con SHA-256 sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; // Calcular la firma sig.computeSignature(xmlString); // Obtener la firma en formato XML const xmlWithSignature = sig.getSignedXml(); xmlFirmado += xmlWithSignature; resolve(xmlFirmado); } catch (e) { console.error(e); reject(e); } finally { if (dsig != null) { //dsig.closeSession(); } } })); }); } signEvento(xmlString, tag, file, password) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var dsig = null; try { //var separator = "_SEPARATOR_"; this.openFile(file, password); let certificate = this.getCertificate(); // Crear un objeto SignedXml // Configurar la clave privada para firmar (ejemplo, deberías cargar tu propia clave privada) let xmlFirmado = ""; const sig = new SignedXml({ publicKey: this.getCertificate(), privateKey: this.getPrivateKey(), passphrase: password, getKeyInfoContent: (publicKey, prefix) => { const certContent = certificate.replace(/(?:\r\n|\r|\n)/g, ""); // Remover saltos de línea del certificado return `<X509Data><X509Certificate>${certContent}</X509Certificate></X509Data>`; }, }); const jsonXML = yield xml2js.parseStringPromise(xmlString); const idAtributo = jsonXML["env:Envelope"]["env:Body"][0]["rEnviEventoDe"][0]["dEvReg"][0]["gGroupGesEve"][0]["rGesEve"][0][tag][0].$.Id; sig.addReference( /*"#" + idAtributo, */ { xpath: "//*[local-name()='" + tag + "']", digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; // Algoritmo de firma RSA con SHA-256 sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; // Calcular la firma sig.computeSignature(xmlString, { location: { reference: "//*[local-name()='" + tag + "']", action: "after", }, }); // Obtener la firma en formato XML const xmlWithSignature = sig.getSignedXml(); xmlFirmado += xmlWithSignature; resolve(xmlFirmado); } catch (e) { console.error(e); reject(e); } finally { if (dsig != null) { //dsig.closeSession(); } } })); }); } openCertificate(file) { if (fs.existsSync(file)) { const pkcs12 = fs.readFileSync(file); this.p12Asn1 = node_forge_1.default.asn1.fromDer(pkcs12.toString("binary")); } else { throw Error(file + " no encontrado!"); } } openFile(file, passphase) { this.openCertificate(file); this.p12 = node_forge_1.default.pkcs12.pkcs12FromAsn1(this.p12Asn1, false, passphase); } cleanCertificate() { this.p12 = undefined; } getCertificate() { for (let i = 0; i < this.p12.safeContents.length; i++) { if (this.p12.safeContents[i].safeBags[0].cert) { const b64 = node_forge_1.default.pki.certificateToPem(this.p12.safeContents[i].safeBags[0].cert); const l = b64.split("\n"); l.pop(); l.pop(); l[0] = ""; return l.join("\n"); } } return null; } getPrivateKey() { for (let i = 0; i < this.p12.safeContents.length; i++) { if (this.p12.safeContents[i].safeBags[0].key) { return node_forge_1.default.pki.privateKeyToPem(this.p12.safeContents[i].safeBags[0].key); } } return null; } getExpiration(file, password) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { try { let validity = { notBefore: "1970-01-01 00:00:00", notAfter: "1970-01-01 00:00:00", }; if (fs.existsSync(file)) { const p12File = fs.readFileSync(file); const p12Asn1 = node_forge_1.default.asn1.fromDer(node_forge_1.default.util.createBuffer(p12File.toString("binary"))); const p12 = node_forge_1.default.pkcs12.pkcs12FromAsn1(p12Asn1, password); // Cambia 'your-password' por la contraseña de tu archivo .p12 const certBag1 = p12.getBags({ bagType: node_forge_1.default.pki.oids.certBag, }); const certBag = certBag1[node_forge_1.default.pki.oids.certBag][0]; const certificate = certBag.cert; validity = certificate.validity; } resolve(validity); } catch (error) { reject(error); } })); }); } } exports.default = new XMLDsigNode(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWE1MRHNpZ05vZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvWE1MRHNpZ05vZGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUFBQSw0REFBNEQ7QUFDNUQsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3pCLE1BQU0sRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3pELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUN6QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7QUFFakMsNERBQStCO0FBRS9CLE1BQU0sV0FBVztJQUlGLGFBQWEsQ0FDeEIsSUFBZ0IsRUFDaEIsR0FBUSxFQUNSLElBQVMsRUFDVCxRQUFhOztZQUViLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBTyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7Z0JBQzNDLElBQUksSUFBSSxHQUFHLElBQUksQ0FBQztnQkFDaEIsSUFBSTtvQkFDRixJQUFJLFNBQVMsR0FBRyxhQUFhLENBQUM7b0JBQzlCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO29CQUU5QixJQUFJLFdBQVcsR0FBUSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7b0JBRTdDLDRCQUE0QjtvQkFFNUIsNkZBQTZGO29CQUU3RixJQUFJLFVBQVUsR0FBRyxFQUFFLENBQUM7b0JBQ3BCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO3dCQUNwQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBRTFCLE1BQU0sR0FBRyxHQUFHLElBQUksU0FBUyxDQUFDOzRCQUN4QixTQUFTLEVBQUUsSUFB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