UNPKG

ec-sri-invoice-signer

Version:
165 lines (164 loc) 7.71 kB
"use strict"; 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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __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) => { const md = forge.md.sha1.create(); md.update(data, 'utf8'); return forge.util.encode64(privateKey.sign(md)); }; exports.sign = sign; const getHash = (data) => { const md = forge.md.sha1.create(); md.update(data, 'utf8'); return forge.util.encode64(md.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|private|key/i.test(name) || !name; }); if (!privateKeyBag) { throw new errors_1.UnsuportedPkcs12Error("No private key bag found in BCE .p12"); } const privateKey = privateKeyBag.key; if (!privateKey) { throw new errors_1.UnsuportedPkcs12Error("No valid key found in BCE .p12"); } return privateKey; }; const extractPrivateKeyAndCertificateFromPkcs12 = (pkcs12RawData, password = '') => { var _a, _b, _c; 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) { throw new errors_1.UnsuportedPkcs12Error("No certificate found in PKCS#12"); } if (!pkcs8ShroudedKeyBags || Object.keys(pkcs8ShroudedKeyBags).length === 0) { throw new errors_1.UnsuportedPkcs12Error("No private key found in PKCS#12"); } 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 certificate"); } let privateKey = null; // Extract private key const allKeyBags = [ ...(pkcs8ShroudedKeyBags[forge.pki.oids.pkcs8ShroudedKeyBag] || []), ...(pkcs8ShroudedKeyBags[forge.pki.oids.keyBag] || []), ]; // If it's a BCE certificate, use the specific extraction if (/banco central/i.test(friendlyName) || /eci|bce/i.test(friendlyName)) { privateKey = getBancoCentralPkcs12PrivateKey(allKeyBags); } else { const firstKeyBag = allKeyBags[0]; privateKey = (firstKeyBag === null || firstKeyBag === void 0 ? void 0 : firstKeyBag.key) ? firstKeyBag.key : null; } if (!privateKey) { throw new errors_1.UnsuportedPkcs12Error("Couldn't find private key"); } // Also extract the full certificate chain if available const caBags = p12.getBags({ bagType: forge.pki.oids.certBag }); const certChain = (caBags[forge.pki.oids.certBag] || []) .map(bag => bag.cert) .filter(cert => cert !== undefined); return { privateKey, certificate, certChain // Full chain for validation }; }; 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) => { // reverse to follow convention of country-name-is-last seen in the SRI example const attributes = certificate.issuer.attributes .filter((attr) => attr.shortName || attr.type) .reverse(); return attributes.map((attr) => { const name = attr.shortName ? normalizeIssuerAttributeShortName(attr.shortName) : attr.type; const value = attr.value || ''; return `${name}=${value}`; }).join(','); }; 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 = forge.util.encode64(forge.util.hexToBytes(privateKey.n.toString(16))); const exponent = forge.util.encode64(forge.util.hexToBytes(privateKey.e.toString(16))); return { modulus, exponent }; }; exports.extractPrivateKeyData = extractPrivateKeyData;