Certificate & XML Signing
Brazilian electronic invoices require digital signing with an A1 e-CNPJ certificate in PFX format. This module handles PFX extraction via openssl, certificate info display, and XML digital signature per the NF-e MOC 4.00 specification.
Overview
Brazilian electronic invoices require digital signing with an A1 e-CNPJ certificate (PFX/PKCS#12 format). The module handles PFX extraction, certificate info display, and XML digital signature following the NF-e MOC 4.00 specification.
Files: certificate.ts, sefaz-transport.ts (reuses certificate extraction)
PFX Handling
Why openssl?
Bun's native crypto cannot parse PKCS#12 (PFX) files — the pkcs12 module is not implemented. The workaround uses openssl CLI via child_process:
# Extract certificate (PEM)
openssl pkcs12 -in cert.pfx -clcerts -nokeys -passin pass:xxx -legacy
# Extract private key (PEM)
openssl pkcs12 -in cert.pfx -nocerts -nodes -passin pass:xxx -legacyThe -legacy flag is required for older PFX files that use legacy encryption (RC2-40).
extractCertFromPfx / extractKeyFromPfx
export function extractCertFromPfx(pfxBuffer: Buffer, passphrase: string): string
export function extractKeyFromPfx(pfxBuffer: Buffer, passphrase: string): stringBoth:
- Write PFX buffer to a temp file
- Run
openssl pkcs12to extract PEM - Parse output with regex to isolate the PEM block (
-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----) - Clean up temp file in
finally - Throw if PEM block not found
These functions are exported and shared with sefaz-transport.ts (which needs PEM files for curl mTLS).
loadCertificate
export function loadCertificate(pfx: Buffer, passphrase: string): CertificateDataReturns { privateKey, certificate, pfxBuffer, passphrase } — the PEM strings needed for signing and the raw PFX for storage.
getCertificateInfo
export function getCertificateInfo(pfx: Buffer, passphrase: string): CertificateInfoExtracts human-readable certificate details for the settings UI:
commonName— company name from the certificatevalidFrom,validUntil— validity datesserialNumber— certificate serialissuer— CA (Certificate Authority) name
Uses node:crypto.X509Certificate (available in Bun).
XML Signing
signXml
export function signXml(xml: string, privateKeyPem: string, certificatePem: string): stringSigns an NF-e XML document per the MOC 4.00 signature specification:
- Reference: The
<infNFe Id="NFe...">element is the signed reference - Canonicalization: Exclusive C14N (
http://www.w3.org/2001/10/xml-exc-c14n#) - Transforms:
- Enveloped signature (removes
<Signature>before hashing) - Exclusive C14N
- Enveloped signature (removes
- Digest: SHA-1 (
http://www.w3.org/2000/09/xmldsig#sha1) - Signature algorithm: RSA-SHA1 (
http://www.w3.org/2000/09/xmldsig#rsa-sha1) - KeyInfo: Contains
<X509Data><X509Certificate>with Base64 cert (no headers)
Signature placement
The <Signature> element is inserted inside <NFe>, after <infNFe>:
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe35..." versao="4.00">
...
</infNFe>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="..." />
<SignatureMethod Algorithm="..." />
<Reference URI="#NFe35...">
<Transforms>...</Transforms>
<DigestMethod Algorithm="..." />
<DigestValue>...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIGxTCCBa...</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</NFe>Library
Uses xml-crypto for the actual signing operation. The library handles canonicalization, hashing, and signature computation.
Security notes
- PFX passwords are stored encrypted in the database (
certificate_passwordfield) - Temp files are always cleaned up in
finallyblocks - PEM private keys are never persisted to disk beyond the signing operation
- Certificate expiration is tracked in
certificate_valid_untilfor UI warnings
SEFAZ Communication
Communication with Brazil's SEFAZ tax authority uses SOAP 1.2 over HTTPS with mutual TLS, with each state having its own SEFAZ or delegating to SVRS, supporting authorization, cancellation, events, and more.
Value Objects
Value objects encapsulate complex validation and formatting rules for core NF-e concepts like the 44-digit access key and CPF/CNPJ tax identifiers, providing immutable, equality-by-value abstractions.