import * as forge from 'node-forge'; export interface PemBundle { privateKeyPem: string; certPem: string; chainPem: string; } export function pemToPfx(privateKeyPem: string, certPem: string, chainPem: string, password = ''): Buffer { const key = forge.pki.privateKeyFromPem(privateKeyPem); const cert = forge.pki.certificateFromPem(certPem); const chain = chainPem .split(/(?=-----BEGIN CERTIFICATE-----)/) .filter(Boolean) .map(p => forge.pki.certificateFromPem(p)); const p12 = forge.pkcs12.toPkcs12Asn1(key, [cert, ...chain], password, { algorithm: '3des' }); const der = forge.asn1.toDer(p12).getBytes(); return Buffer.from(der, 'binary'); } export function pfxToPem(pfxBuffer: Buffer, password = ''): PemBundle { const p12Asn1 = forge.asn1.fromDer(forge.util.createBuffer(pfxBuffer.toString('binary'))); const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password); const keyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag }); const certBags = p12.getBags({ bagType: forge.pki.oids.certBag }); const keyBag = keyBags[forge.pki.oids.pkcs8ShroudedKeyBag]?.[0]; const allCertBags = certBags[forge.pki.oids.certBag] ?? []; if (!keyBag?.key) throw new Error('No private key found in PFX'); if (allCertBags.length === 0) throw new Error('No certificates found in PFX'); const [first, ...rest] = allCertBags.map(bag => forge.pki.certificateToPem(bag.cert!)); return { privateKeyPem: forge.pki.privateKeyToPem(keyBag.key), certPem: first, chainPem: rest.join(''), }; } export function parsePemBundle(bundle: string): PemBundle { const blocks = bundle.match(/-----BEGIN [^-]+-----[\s\S]+?-----END [^-]+-----/g) ?? []; const privateKeyPem = blocks.find(b => b.includes('PRIVATE KEY')) ?? ''; const certs = blocks.filter(b => b.includes('CERTIFICATE')); return { privateKeyPem, certPem: certs[0] ?? '', chainPem: certs.slice(1).join(''), }; }