import { TokenCredential } from '@azure/identity'; import { CertificateClient, CertificatePolicy, KeyVaultCertificateWithPolicy, } from '@azure/keyvault-certificates'; import { SecretClient } from '@azure/keyvault-secrets'; export class KeyVaultStore { private readonly secretClient: SecretClient; private readonly certClient: CertificateClient; constructor(credential: TokenCredential, keyVaultUrl: string) { this.secretClient = new SecretClient(keyVaultUrl, credential); this.certClient = new CertificateClient(keyVaultUrl, credential); } async getSecret(name: string): Promise { try { const secret = await this.secretClient.getSecret(name); return secret.value; } catch (err: unknown) { if (isNotFound(err)) return undefined; throw err; } } async setSecret(name: string, value: string): Promise { await this.secretClient.setSecret(name, value); } async getCertificate(name: string): Promise { try { return await this.certClient.getCertificate(name); } catch (err: unknown) { if (isNotFound(err)) return undefined; throw err; } } async certificateExpiresWithin(name: string, days: number): Promise { const cert = await this.getCertificate(name); if (!cert) return 'missing'; const expiresOn = cert.properties.expiresOn; if (!expiresOn) return false; const thresholdMs = days * 24 * 60 * 60 * 1000; return expiresOn.getTime() - Date.now() <= thresholdMs; } async importCertificate(name: string, cert: string | Buffer, format: 'pem' | 'pfx' = 'pem', password?: string): Promise { const certBuffer = typeof cert === 'string' ? Buffer.from(cert) : cert; const contentType = format === 'pfx' ? 'application/x-pkcs12' : 'application/x-pem-file'; try { // When a certificate already exists, Azure validates the incoming bytes against // its stored policy's content_type. Updating the policy first tells Azure to // expect the new format; without this, converting PEM→PFX (or vice-versa) // fails because Azure tries to parse binary PFX data as PEM. await this.certClient.updateCertificatePolicy(name, { contentType } as CertificatePolicy); } catch { // Certificate doesn't exist yet — no policy to update, proceed to import. } await this.certClient.importCertificate(name, certBuffer, { password }); } } function isNotFound(err: unknown): boolean { return ( typeof err === 'object' && err !== null && 'statusCode' in err && (err as { statusCode: number }).statusCode === 404 ); }