fix: add password parameter to pemToPfx and importCertificate functions for enhanced security

This commit is contained in:
2026-05-22 14:36:43 +02:00
parent 17fecaca22
commit 2f6db155cb
3 changed files with 14 additions and 9 deletions
+2 -1
View File
@@ -47,8 +47,9 @@ export class KeyVaultStore {
return expiresOn.getTime() - Date.now() <= thresholdMs;
}
async importCertificate(name: string, cert: string | Buffer, format: 'pem' | 'pfx' = 'pem'): Promise<void> {
async importCertificate(name: string, cert: string | Buffer, format: 'pem' | 'pfx' = 'pem', password?: string): Promise<void> {
const options: ImportCertificateOptions = {
password,
policy: {
contentType: format === 'pfx' ? 'application/x-pkcs12' : 'application/x-pem-file',
issuerName: 'Unknown',
+2 -2
View File
@@ -6,7 +6,7 @@ export interface PemBundle {
chainPem: string;
}
export function pemToPfx(privateKeyPem: string, certPem: string, chainPem: string): Buffer {
export function pemToPfx(privateKeyPem: string, certPem: string, chainPem: string, password: string): Buffer {
const key = forge.pki.privateKeyFromPem(privateKeyPem);
const cert = forge.pki.certificateFromPem(certPem);
const chain = chainPem
@@ -14,7 +14,7 @@ export function pemToPfx(privateKeyPem: string, certPem: string, chainPem: strin
.filter(Boolean)
.map(p => forge.pki.certificateFromPem(p));
const p12 = forge.pkcs12.toPkcs12Asn1(key, [cert, ...chain], null);
const p12 = forge.pkcs12.toPkcs12Asn1(key, [cert, ...chain], password, { algorithm: '3des' });
const der = forge.asn1.toDer(p12).getBytes();
return Buffer.from(der, 'binary');
}
+10 -6
View File
@@ -1,3 +1,4 @@
import { randomUUID } from 'node:crypto';
import { DefaultAzureCredential } from '@azure/identity';
import { AcmeClient } from './acme.js';
import { ChallengeHandler } from './challenge.js';
@@ -105,10 +106,12 @@ export class Provisioner {
const fqdns = group.map(d => d.fqdn);
const issued = await this.acme.orderCertificate(fqdns, this.challengeHandler);
const [cert, format] = this.config.pfx
? [pemToPfx(issued.privateKeyPem, issued.certificatePem, issued.chainPem), 'pfx' as const]
: [issued.privateKeyPem + issued.certificatePem + issued.chainPem, 'pem' as const];
await this.store.importCertificate(certName, cert, format);
if (this.config.pfx) {
const password = randomUUID();
await this.store.importCertificate(certName, pemToPfx(issued.privateKeyPem, issued.certificatePem, issued.chainPem, password), 'pfx', password);
} else {
await this.store.importCertificate(certName, issued.privateKeyPem + issued.certificatePem + issued.chainPem, 'pem');
}
if (status === 'missing') {
result.certificatesIssued.push(primary.fqdn);
@@ -157,9 +160,10 @@ export class Provisioner {
if (currentFormat === 'pem') {
const bundle = parsePemBundle(secretValue);
this.log(`[convert] parsed blocks — key: ${bundle.privateKeyPem.length} chars, cert: ${bundle.certPem.length} chars, chain: ${bundle.chainPem.length} chars`);
const pfxBuffer = pemToPfx(bundle.privateKeyPem, bundle.certPem, bundle.chainPem);
const password = randomUUID();
const pfxBuffer = pemToPfx(bundle.privateKeyPem, bundle.certPem, bundle.chainPem, password);
this.log(`[convert] PFX buffer size: ${pfxBuffer.length} bytes`);
await this.store.importCertificate(certName, pfxBuffer, 'pfx');
await this.store.importCertificate(certName, pfxBuffer, 'pfx', password);
} else {
const bundle = pfxToPem(Buffer.from(secretValue, 'base64'));
this.log(`[convert] parsed PFX — key: ${bundle.privateKeyPem.length} chars, cert: ${bundle.certPem.length} chars, chain: ${bundle.chainPem.length} chars`);