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
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 });
//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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