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.
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:
| OID | Algorithm | Numeric ID | Key length |
|---|---|---|---|
2b 0e 03 02 1a | SHA-1 | 1 | 20 bytes |
60 86 48 01 65 03 04 02 04 | SHA-224 | 224 | 28 bytes |
60 86 48 01 65 03 04 02 01 | SHA-256 | 256 | 32 bytes |
60 86 48 01 65 03 04 02 02 | SHA-384 | 384 | 48 bytes |
60 86 48 01 65 03 04 02 03 | SHA-512 | 512 | 64 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:
| Field | Meaning |
|---|---|
macAlgo | Numeric digest ID from the OID table above |
keyLen | HMAC key length in bytes |
iterations | PBKDF2 iteration count from MacData |
saltLen | Length of macSalt in bytes |
salt | macSalt hex-encoded |
authSafeData | The full authSafe OCTET STRING, hex-encoded |
storedHmac | The 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 key2048— PBKDF2 iteration count8— 8-byte salt2e14f495758be090— themacSalt<authSafeData>— the fullauthSafepayload (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.