ts-spiffe
Version:
typescript client for spiffe
147 lines • 6.82 kB
JavaScript
;
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