ec-sri-invoice-signer
Version:
Ecuador SRI invoice signer.
135 lines (134 loc) • 6.91 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractX509Data = exports.extractIssuerData = exports.extractPrivateKeyData = exports.extractPrivateKeyAndCertificateFromPkcs12 = exports.getHash = exports.sign = void 0;
const forge = __importStar(require("node-forge"));
const errors_1 = require("./errors");
const sign = (data, privateKey) => {
return forge.util.encode64(privateKey.sign(forge.sha1.create().update(data, 'utf8')));
};
exports.sign = sign;
const getHash = (data) => {
return forge.util.encode64(forge.sha1.create().update(data, 'utf8').digest().bytes());
};
exports.getHash = getHash;
const getBancoCentralPkcs12PrivateKey = (pkcs8ShroudedKeyBags) => {
const privateKeyBag = pkcs8ShroudedKeyBags.find((bag) => {
var _a, _b;
const name = (_b = (_a = bag === null || bag === void 0 ? void 0 : bag.attributes) === null || _a === void 0 ? void 0 : _a.friendlyName) === null || _b === void 0 ? void 0 : _b[0];
return /signing key/i.test(name);
});
if (!privateKeyBag) {
throw new errors_1.UnsuportedPkcs12Error("No key bag with friendly name 'Signing Key' found in the key bags of 'Banco Central del Ecuador' .p12");
}
const privateKey = privateKeyBag.key;
if (!privateKey) {
throw new errors_1.UnsuportedPkcs12Error("No valid key found in 'Banco Central del Ecuador' .p12");
}
return privateKey;
};
const extractPrivateKeyAndCertificateFromPkcs12 = (pkcs12RawData, password = '') => {
var _a, _b, _c, _d, _e;
const pkcs12InBase64 = typeof pkcs12RawData === 'string' ? pkcs12RawData : pkcs12RawData.toString('base64');
const pkcs12InDer = forge.util.decode64(pkcs12InBase64);
const p12Asn1 = forge.asn1.fromDer(pkcs12InDer);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);
const certBags = p12.getBags({ bagType: forge.pki.oids.certBag });
const certBag = (_a = certBags[forge.pki.oids.certBag]) === null || _a === void 0 ? void 0 : _a[0];
const pkcs8ShroudedKeyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag });
if (!certBag || !pkcs8ShroudedKeyBags) {
throw new errors_1.UnsuportedPkcs12Error();
}
const friendlyName = (_c = (_b = certBag === null || certBag === void 0 ? void 0 : certBag.attributes) === null || _b === void 0 ? void 0 : _b.friendlyName) === null || _c === void 0 ? void 0 : _c[0];
const certificate = certBag.cert;
if (!certificate) {
throw new errors_1.UnsuportedPkcs12Error("Couldn't find its certificate");
}
let privateKey = null;
if (/banco central/i.test(friendlyName)) {
privateKey = getBancoCentralPkcs12PrivateKey((_d = pkcs8ShroudedKeyBags[forge.pki.oids.pkcs8ShroudedKeyBag]) !== null && _d !== void 0 ? _d : []);
}
else {
const firstPkcs8ShroudedKeyBag = (_e = pkcs8ShroudedKeyBags[forge.pki.oids.pkcs8ShroudedKeyBag]) === null || _e === void 0 ? void 0 : _e[0];
privateKey = (firstPkcs8ShroudedKeyBag === null || firstPkcs8ShroudedKeyBag === void 0 ? void 0 : firstPkcs8ShroudedKeyBag.key) ? firstPkcs8ShroudedKeyBag.key : null;
}
if (!privateKey) {
throw new errors_1.UnsuportedPkcs12Error("Couldn't find its private key");
}
return {
privateKey,
certificate
};
};
exports.extractPrivateKeyAndCertificateFromPkcs12 = extractPrivateKeyAndCertificateFromPkcs12;
const normalizeIssuerAttributeShortName = (shortName) => {
switch (shortName) {
case 'E':
// As required by the SRI validator code in this line (https://github.com/gdiazs/MITyCLib/blob/master/MITyCLibXADES/src/main/java/es/mityc/firmaJava/libreria/xades/ValidarFirmaXML.java#L2139).
// X500Principal needs EMAILADDRESS instead of E (https://docs.oracle.com/javase/7/docs/api/javax/security/auth/x500/X500Principal.html#X500Principal(java.lang.String)) and has been seen that some certificate issuers set the email address with 'E' shortName.
// The oid for email (1.2.840.113549.1.9.1) could also be used, but that's a bit cryptic and we know the SRI accepts EMAILADDRESS without issue so no need to be that generic.
return 'EMAILADDRESS';
default:
return shortName;
}
;
};
const extractIssuerData = (certificate) => {
const issuerName = certificate.issuer.attributes.reverse().filter((attr) => attr.shortName || attr.type).map((attr) => {
if (attr.shortName) {
const normalizedShortName = normalizeIssuerAttributeShortName(attr.shortName);
return `${normalizedShortName}=${attr.value}`;
}
else {
return `${attr.type}=${attr.value}`;
}
}).join(','); // .reverse to follow convention of country-name-is-last seen in the SRI example
return issuerName;
};
exports.extractIssuerData = extractIssuerData;
const extractX509Data = (certificate) => {
const serialNumber = new forge.jsbn.BigInteger(Array.from(Buffer.from(certificate.serialNumber, 'hex'))).toString();
const issuerName = extractIssuerData(certificate);
const certificateAsAsn1 = forge.pki.certificateToAsn1(certificate);
const contentAsDer = forge.asn1.toDer(certificateAsAsn1);
const contentHash = forge.util.encode64(forge.sha1.create().update(contentAsDer.bytes()).digest().bytes());
const content = forge.util.encode64(contentAsDer.bytes());
return {
content,
contentHash,
issuerName,
serialNumber
};
};
exports.extractX509Data = extractX509Data;
const extractPrivateKeyData = (privateKey) => {
const modulus = Buffer.from(privateKey.e.toString(), 'hex').toString('base64');
const exponent = Buffer.from(privateKey.n.toString(), 'hex').toString('base64');
return {
modulus,
exponent
};
};
exports.extractPrivateKeyData = extractPrivateKeyData;