PKCS
PFX / PKCS#12
Create a PFX bundle
ts
import {
createSelfSignedCertificate,
unwrap,
} from 'micro509';
import { createPfx } from 'micro509/pkcs';
const ca = await createSelfSignedCertificate({
subject: { commonName: 'Demo CA' },
extensions: { basicConstraints: { ca: true } },
});
const cert = await createSelfSignedCertificate({
subject: { commonName: 'leaf.example' },
});
// createPfx returns a typed result; unwrap once inputs are validated
const pfx = unwrap(
await createPfx({
certificates: [
{ certificate: cert.certificate.pem },
{ certificate: ca.certificate.pem },
],
privateKeys: [{ privateKey: cert.keyPair.privateKey }],
encryption: { password: 'secret' },
mac: { password: 'secret' },
}),
);
console.log(`\
der bytes: ${pfx.der.length}
base64 len: ${pfx.base64.length}`);
console.log(pfx.pem);Parse a PFX bundle
ts
import {
createSelfSignedCertificate,
unwrap,
} from 'micro509';
import { createPfx, parsePfxDer } from 'micro509/pkcs';
// Build a PFX inline to parse back
const cert = await createSelfSignedCertificate({
subject: { commonName: 'leaf.example' },
});
const pfx = unwrap(
await createPfx({
certificates: [{ certificate: cert.certificate.pem }],
privateKeys: [{ privateKey: cert.keyPair.privateKey }],
encryption: { password: 'secret' },
mac: { password: 'secret' },
}),
);
const result = await parsePfxDer(pfx.der, {
password: 'secret',
});
if (result.ok) {
const { certificates, privateKeys, bags } = result.value;
const leafCert = certificates[0];
console.log(`\
certs: ${certificates.length}
private keys: ${privateKeys.length}
bags: ${bags.length}
subject: ${leafCert?.subject.values.commonName}
`);
} else {
console.log(`parse failed: ${result.error.code}`);
}PKCS#7 / CMS
Create a certificate bag
ts
import {
createSelfSignedCertificate,
unwrap,
} from 'micro509';
import { createPkcs7CertBag } from 'micro509/pkcs';
// Two real certificates to bundle
const a = await createSelfSignedCertificate({
subject: { commonName: 'a.example' },
});
const b = await createSelfSignedCertificate({
subject: { commonName: 'b.example' },
});
// createPkcs7CertBag returns a typed result; unwrap on the success path
const bag = unwrap(
createPkcs7CertBag([
a.certificate.pem,
b.certificate.pem,
]),
);
console.log(`der bytes: ${bag.der.length}`);
console.log(bag.pem);Parse a certificate bag
ts
import {
createSelfSignedCertificate,
unwrap,
} from 'micro509';
import {
createPkcs7CertBag,
parsePkcs7CertBagPem,
} from 'micro509/pkcs';
// Build a real cert bag inline
const a = await createSelfSignedCertificate({
subject: { commonName: 'a.example' },
});
const b = await createSelfSignedCertificate({
subject: { commonName: 'b.example' },
});
const bag = unwrap(
createPkcs7CertBag([
a.certificate.pem,
b.certificate.pem,
]),
);
const result = parsePkcs7CertBagPem(bag.pem);
if (result.ok) {
const certificates = result.value;
console.log(`certs: ${certificates.length}`);
for (const cert of certificates) {
console.log(cert.subject.values.commonName);
}
} else {
console.log(`parse failed: ${result.error.code}`);
}Sign and verify content
ts
import { createSelfSignedCertificate } from 'micro509';
import {
createPkcs7SignedData,
verifyPkcs7SignedData,
} from 'micro509/pkcs';
// A signer is a certificate + its matching private key
const signer = await createSelfSignedCertificate({
subject: { commonName: 'signer.example' },
extensions: { keyUsage: ['digitalSignature'] },
});
// Sign content -> attached CMS SignedData (RFC 5652)
const content = new TextEncoder().encode('hello');
const signed = await createPkcs7SignedData({
content,
signers: [
{
certificate: signer.certificate.pem,
privateKey: signer.keyPair.privateKey,
},
],
});
// Creation returns a typed result; verify on success
if (!signed.ok) {
console.log(`sign failed: ${signed.error.code}`);
} else {
const result = await verifyPkcs7SignedData(
signed.value.pem,
);
if (result.ok) {
const sd = result.value;
const info = sd.signerInfos[0];
console.log(`\
verified: true
signers: ${sd.signerInfos.length}
digest: ${info?.digestAlgorithmName}
signature: ${info?.signatureAlgorithmName}
sig hex: ${info?.signatureHex.slice(0, 24)}…
der size: ${signed.value.der.length} bytes`);
} else {
console.log(`verify: ${result.error.code}`);
}
}Detached signatures
Pass detached: true to omit the content from the SignedData (RFC 5652 §5.2): the signature still covers it via the messageDigest signed attribute, but the verifier must supply the bytes externally. This is the shape git x509 commit signing and S/MIME detached signatures use — git stores only the CMS blob in the commit header and provides the commit bytes at verification time.
ts
import { createSelfSignedCertificate } from 'micro509';
import {
createPkcs7SignedData,
verifyPkcs7SignedData,
} from 'micro509/pkcs';
const signer = await createSelfSignedCertificate({
subject: { commonName: 'detached.example' },
extensions: { keyUsage: ['digitalSignature'] },
});
const content = new TextEncoder().encode('tree abc…');
const signed = await createPkcs7SignedData({
content,
detached: true,
signers: [
{
certificate: signer.certificate.pem,
privateKey: signer.keyPair.privateKey,
},
],
});
if (!signed.ok) {
console.log(`sign failed: ${signed.error.code}`);
} else {
// Without the content, verification fails typed:
const missing = await verifyPkcs7SignedData(
signed.value.der,
);
// Tampered content: digest check catches it
const forged = await verifyPkcs7SignedData(
signed.value.der,
{ content: new TextEncoder().encode('tree evil…') },
);
// The externally-held original bytes verify
const result = await verifyPkcs7SignedData(
signed.value.der,
{ content },
);
const info = result.ok
? result.value.signerInfos[0]
: undefined;
console.log(`\
blob size: ${signed.value.der.length} bytes (no eContent)
no content: ok=${missing.ok} (${missing.ok ? '' : missing.error.code})
forged: ok=${forged.ok} (${forged.ok ? '' : forged.error.code})
original: ok=${result.ok}
signature: ${info?.signatureAlgorithmName}
digest: ${info?.digestAlgorithmName}`);
}When the SignedData embeds its own content (the attached default), the embedded content is what gets verified and options.content is ignored.
PEM utilities
ts
import {
createSelfSignedCertificate,
unwrap,
} from 'micro509';
import {
categorizePemBlocks,
pemDecode,
pemEncode,
splitPemBlocks,
} from 'micro509/pem';
// A real certificate to feed the PEM helpers
const { certificate } = await createSelfSignedCertificate({
subject: { commonName: 'pem.example' },
});
const pem = certificate.pem;
// Decode a single PEM block to DER (typed result)
const der = unwrap(pemDecode('CERTIFICATE', pem));
// Encode DER back to PEM
const pemEncoded = pemEncode('CERTIFICATE', der);
// Split a multi-block PEM file
const multiPem = `${pem}\n${pem}`;
const blocks = unwrap(splitPemBlocks(multiPem));
// Categorize blocks by type
const { certificates, certificateRequests, privateKeys } =
unwrap(categorizePemBlocks(multiPem));
console.log(`\
der bytes: ${der.length}
der head: ${[...der.slice(0, 8)]
.map((byte) => byte.toString(16).padStart(2, '0'))
.join(' ')}
round-trip: ${pemEncoded === pem}
blocks: ${blocks.map((block) => block.label).join(', ')}
certs: ${certificates.length}
csrs: ${certificateRequests.length}
private keys: ${privateKeys.length}`);