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
JavaScript
;
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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