Skip to content

How PKCS#12 / PFX hashes are extracted

How Hash Extractor walks the DER PFX structure to pull out the $pfxng$ MAC hash for cracking with John the Ripper's pfx format.

Published on 4 min read

PKCS#12 bundles — the .p12 and .pfx files used to carry TLS certificates, client authentication credentials, and code-signing keys — are protected by a MAC integrity password. That password is what gets cracked. Hash Extractor parses the DER structure entirely in your browser and emits the $pfxng$ hash that John the Ripper's pfx format understands.

What is actually being cracked

A PKCS#12 file is not encrypted at the outer layer — its authSafe content is plain DER data. What the password protects is a keyed-HMAC stored in the MacData field at the end of the file. The MAC is computed over the authSafe bytes using a key derived from the password via PBKDF2 (or PBMAC1 for newer files). To verify a candidate password, a cracker re-derives the HMAC key and checks it against the stored digest. That check is the crackable operation.

The DER PFX structure (RFC 7292)

A PKCS#12 file is a single DER-encoded SEQUENCE called PFX:

PFX ::= SEQUENCE {
    version     INTEGER,
    authSafe    ContentInfo,
    macData     MacData OPTIONAL
}

The parser opens the top-level SEQUENCE and indexes its three children.

authSafe ContentInfo

authSafe is a ContentInfo SEQUENCE. Its first child is a contentType OID that must equal pkcs7-data (1.2.840.113549.1.7.1, DER body 2a 86 48 86 f7 0d 01 07 01). Files that carry a different content type — such as encrypted-data bundles — are not MAC-integrity-protected and are rejected as unsupported.

When the content type matches, a context [0] wrapper follows, containing an OCTET STRING. The raw bytes of that OCTET STRING are the authSafe data: the exact blob that was MACed. The parser captures this as auth_data.

MacData

The third child of the top SEQUENCE is MacData:

MacData ::= SEQUENCE {
    mac         DigestInfo,
    macSalt     OCTET STRING,
    iterations  INTEGER DEFAULT 1
}

The DigestInfo inside mac carries an AlgorithmIdentifier (which contains an OID identifying the digest algorithm) and an OCTET STRING holding the stored HMAC value. The parser walks into the AlgorithmIdentifier SEQUENCE, finds the OID tag (0x06), and maps the OID bytes to a numeric algorithm ID and key length.

Digest OID mapping

The supported digest OIDs and their $pfxng$ numeric codes are:

OIDAlgorithmNumeric IDKey length
2b 0e 03 02 1aSHA-1120 bytes
60 86 48 01 65 03 04 02 04SHA-22422428 bytes
60 86 48 01 65 03 04 02 01SHA-25625632 bytes
60 86 48 01 65 03 04 02 02SHA-38438448 bytes
60 86 48 01 65 03 04 02 03SHA-51251264 bytes

Any other OID causes the extraction to fail with an unsupported-format error rather than silently producing a wrong hash.

If iterations is absent from MacData the parser defaults to 1, matching the RFC 7292 DEFAULT.

The $pfxng$ hash format

The extracted hash has this shape:

$pfxng$<macAlgo>$<keyLen>$<iterations>$<saltLen>$<salt>$<authSafeData>$<storedHmac>

Each field:

FieldMeaning
macAlgoNumeric digest ID from the OID table above
keyLenHMAC key length in bytes
iterationsPBKDF2 iteration count from MacData
saltLenLength of macSalt in bytes
saltmacSalt hex-encoded
authSafeDataThe full authSafe OCTET STRING, hex-encoded
storedHmacThe HMAC digest from DigestInfo, hex-encoded

A real example

The test suite ships a fixed PKCS#12 file — SHA-256 MAC, password pw, 2048 iterations — generated with OpenSSL. Running the extractor against it produces:

$pfxng$256$32$2048$8$2e14f495758be090$<authSafeData>$9cab3ce344c7fba639d6aa9d1c9c4a1836a2a8d48fde2932eb447dc3502f88d9

Breaking that down against the format:

  • 256 — SHA-256 MAC (numeric ID 256, key length 32)
  • 32 — 32-byte HMAC key
  • 2048 — PBKDF2 iteration count
  • 8 — 8-byte salt
  • 2e14f495758be090 — the macSalt
  • <authSafeData> — the full authSafe payload (several kilobytes in this file)
  • 9cab3ce3... — the stored HMAC that a correct password must reproduce

The authSafeData field is long because it contains the DER-encoded certificate and key bag data — the cracker needs it to recompute the MAC.

Cracking with John the Ripper

There is no dedicated hashcat mode for PKCS#12 MAC. Use John's pfx format:

echo '$pfxng$256$32$2048$8$2e14f495758be090$<authSafeData>$9cab3ce3...' > hash.txt
john --format=pfx --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

John re-derives the HMAC key using PBKDF2 with the candidate password, the stored salt, and the iteration count, then recomputes the HMAC over authSafeData and compares it to storedHmac. The iteration count is the main cost multiplier — 2048 rounds at SHA-256 is comparatively light; production certificates often use higher counts.

Why the extraction is client-side

A .pfx file bundles the private key for a certificate alongside the certificate chain. Uploading it to a remote service to extract a hash would expose that private key to a third party. Hash Extractor compiles the Rust parser to WebAssembly and runs it entirely in your browser. The file never leaves your machine. Only the hash string — which contains no key material, only the MAC parameters — goes to the cracker.

The extraction logic mirrors pfx2john from the John the Ripper project and produces identical $pfxng$ strings, so any existing John workflow works unchanged.

Related articles

Why 7z is the trickiest archive to crack: LZMA-compressed headers, AES coder property encoding, and the full $7z$ hash format explained.
Inside the adb backup text header: how PBKDF2 salts, round counts, and a master-key blob become a crackable $ab$ hash.
A technical walkthrough of how Hash Extractor parses Ansible Vault files to produce a crackable $ansible$ hash for hashcat and John the Ripper.