UNPKG

ts-spiffe

Version:
147 lines 6.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.X509Svid = void 0; const x509_1 = require("@peculiar/x509"); const spiffeid_1 = require("../spiffeid"); const CertificateUtils_1 = require("../internal/CertificateUtils"); const Logger_1 = require("../internal/Logger"); const pkijs_1 = require("pkijs"); const asn1js_1 = require("asn1js"); class X509Svid { constructor(certificate, privateKey, hint, bundle) { this.certificate = certificate; this.privateKey = privateKey; this.hint = hint; this.bundle = bundle; this.logger = new Logger_1.Logger(X509Svid); } ; async getSpiffeId() { return spiffeid_1.SpiffeId.getSpiffeId(await this.getLeaf()); } async getLeaf() { const leafPEM = await this.getLeafAsPEM(); if (leafPEM) { const x509 = await CertificateUtils_1.CertificateUtils.parseX509DER(leafPEM); if (x509) return new x509_1.X509Certificate(x509[0].tbsView.buffer); } throw new Error("unable to get the leaf certificate from the svid"); } async getLeafAsPEM() { // It's likely the raw certificate contains both the leaf and intermediary, with the former used for auth and the latter for digital signature/key encipherment/ key agreement const certResult = await this.getCertificate(Buffer.from(this.certificate).toString("base64")); if (certResult === undefined) throw new Error("no certificate"); const OID = "2.5.29.37"; // Extended Key USage for (let i = 0; i < certResult.length; i++) { const cert = certResult[i]; const extension = cert.extensions?.find((ext) => ext.extnID === OID); if (!extension) { continue; } const extensionValueBER = extension.extnValue.valueBlock.valueHexView; const asn1 = (0, asn1js_1.fromBER)(extensionValueBER); const extKeyUsage = new pkijs_1.ExtKeyUsage({ schema: asn1.result }); const OID_ServerAuth = "1.3.6.1.5.5.7.3.1"; const OID_ClientAuth = "1.3.6.1.5.5.7.3.2"; for (const keyPurposeId of extKeyUsage.keyPurposes) { if (keyPurposeId === OID_ServerAuth || keyPurposeId === OID_ClientAuth) { // OID for serverAuth return this.getPEM(cert); } } } throw new Error("Unable to find leaf certificate that has an Extended Key Usage extension set"); } async getIntermediateAsPEM() { const certResult = await this.getCertificate(Buffer.from(this.certificate).toString("base64")); if (certResult === undefined) throw new Error("no certificate"); const OID = "2.5.29.15"; // Key Usage for (let i = 0; i < certResult.length; i++) { const cert = certResult[i]; const extension = cert.extensions?.find((ext) => ext.extnID === OID); if (!extension) { continue; } const bitStringValue = extension.parsedValue; // Convert the BitString's ArrayBuffer to a Uint8Array const unusedBits = bitStringValue.valueBlock.unusedBits; const bitArray = new Uint8Array(bitStringValue.valueBlock.valueHex); const padding = unusedBits; // Number of bits used for padding // Check the specific bits for each key usage // Note: The bits are in reverse order const digitalSignature = (bitArray[0] & 0b10000000 >>> padding) !== 0; // bit 0 const keyEncipherment = (bitArray[0] & 0b00100000 >>> padding) !== 0; // bit 2 const keyAgreement = (bitArray[0] & 0b00001000 >>> padding) !== 0; // bit 4 if (digitalSignature || keyEncipherment || keyAgreement) { return this.getPEM(cert); } } throw new Error("Unable to find intermediate certificate that has Key Usage extension set"); } async getBundle() { const pem = await this.getBundleAsPEM(); if (pem) return new x509_1.X509Certificate(pkijs_1.Certificate.fromBER(Buffer.from(pem, "utf-8")).tbsView.buffer); throw new Error("unable to get the root certificate from the svid"); } async getBundleAsPEM() { const root = await this.getCertificate(Buffer.from(this.bundle).toString("base64")); if (root === undefined) throw new Error("no root certificate"); return this.getPEM(root[0]); } async getKeyAsPEM() { const key = Buffer.from(this.privateKey).toString("base64"); return this.derToPem(key); } arrayBufferToString(buffer) { return String.fromCharCode.apply(null, Array.from(new Uint8Array(buffer))); } derToPem(derBase64) { const derBuffer = Buffer.from(derBase64, 'base64'); const header = '-----BEGIN PRIVATE KEY-----'; const footer = '-----END PRIVATE KEY-----'; return [header, derBuffer.toString('base64'), footer].join('\n'); } async getCertificate(base64) { return new Promise((resolve, _reject) => { CertificateUtils_1.CertificateUtils.parseX509DER(base64).then((result) => { resolve(result); }); }); } getPEM(rootResult) { const rootDER = rootResult.toSchema().toBER(false); const rootBuf = Buffer.from(new Uint8Array(rootDER).buffer); return `-----BEGIN CERTIFICATE-----\n${rootBuf.toString("base64")}\n-----END CERTIFICATE-----`; } static async getX509Svid(x509svid) { return new X509Svid(x509svid.x509Svid, x509svid.x509SvidKey, x509svid.hint, x509svid.bundle); } static async getX509SvidFromStream(response) { const logger = new Logger_1.Logger(X509Svid); for (const x509svid of response.svids) { if (!x509svid) { continue; } try { const certificate = Buffer.from(x509svid.x509Svid).toString("base64"); const privateKey = x509svid.x509SvidKey; const hint = x509svid.hint; const x509 = await CertificateUtils_1.CertificateUtils.parseX509DER(certificate); if (x509 !== undefined) { return new X509Svid(x509svid.x509Svid, x509svid.x509SvidKey, x509svid.hint, x509svid.bundle); } } catch (error) { logger.error(`${error}`); throw new Error("Could not get certificate from X509SVIDResponse. " + error); } } throw new Error("No certificates were returned from X509SVIDResponse."); } } exports.X509Svid = X509Svid; //# sourceMappingURL=X509Svid.js.map