SefazNfe.Certificate.PKCS12 (sefaz_nfe v0.1.0)

Copy Markdown View Source

PKCS#12 (RFC 7292) reader for ICP-Brasil A1 files. Internal to SefazNfe.Certificate.

OTP 29 ships no PKCS#12 support — :public_key has no pkcs12_to_der — and Hex has no package for it, so the PFX is walked here: the DER structure by hand, :crypto for the ciphers.

Scope

Only algorithms verified byte-for-byte against a real A1 file are accepted: pbeWithSHAAnd3-KeyTripleDES-CBC, which is what ICP-Brasil issues today. Anything else returns {:error, {:unsupported_pbe, oid}}, or {:error, {:unsupported_mac, oid}} for a digest other than SHA-1, rather than an empty result that would read as a certificate with no key. Naming the algorithm matters: reporting a modern PFX as an invalid password would send the caller hunting for the wrong bug.

The PKCS#12 MAC is required, not optional. Without it a wrong password is indistinguishable from a corrupt file, and both must fail before any SOAP call.

Key derivation

kdf/5 is RFC 7292 appendix B.2 with SHA-1: a 20 byte digest over 64 byte blocks, diversified by 1 for the key, 2 for the IV and 3 for the MAC (appendix B.3). The password is a BMPString — UTF-16BE with a two byte null terminator. Each block round is I_j = (I_j + B + 1) mod 2^512, where the modulo falls out of bitstring construction truncating to the declared size.

Summary

Functions

Decodes pfx into the leaf certificate, the PKCS#8 private key and the chain.

Types

parsed()

@type parsed() :: %{der: binary(), key: binary(), chain: [binary()]}

Functions

parse(pfx, password)

@spec parse(binary(), String.t()) :: {:ok, parsed()} | {:error, term()}

Decodes pfx into the leaf certificate, the PKCS#8 private key and the chain.

A structurally broken PFX fails pattern matching deep in the DER walk; that is bad input rather than a caller bug, so it is rescued into {:error, :invalid_certificate} like a wrong password.