PFX and P12 files explained: what is inside, passwords, and getting PEM back out

A .pfx or .p12 file turns up whenever a certificate has to travel withits private key: Azure asks for one, a Java keystore turns out to be one, a vendor emails one with the password in the same message. It is the only mainstream certificate container that holds a secret, and that one fact explains everything awkward about it — the password prompt, the two incompatible encryption generations, the importers that say “incorrect password” when they mean “unsupported cipher”. By the end you will know what the bytes hold, what the password protects, how to tell which flavour you have, and how to unpack, build and re-password one — with the OpenSSL command and the browser tool for each.

What a PKCS#12 file actually is

PKCS#12 is defined in RFC 7292. The outer structure is an ASN.1 SEQUENCE holding a version number (always 3), an AuthenticatedSafe and a MacData, so every real PFX starts with the bytes 30 82 … 02 01 03 and openssl asn1parse -inform DER -in site.pfx | head -4 prints INTEGER :03 then OBJECT :pkcs7-data. The AuthenticatedSafe is a list of “safe contents”, each stored in the clear or encrypted under the password, holding bags: certBag for a certificate, pkcs8ShroudedKeyBag for a private key encrypted under the password, and plain keyBag for an unencrypted key (rare). Two attributes can hang off a bag: friendlyName, the label Windows shows in the certificate store, and localKeyID, a byte string pairing a key bag with its certificate bag — usually the certificate’s SHA-1.

.pfx and .p12are the same bytes — Microsoft kept the extension of its pre-standard “Personal Information Exchange” format. Every tool on this site accepts either.

ContainerCan holdPasswordEncoding
PFX / P12 (PKCS#12)Certificate, chain and private key togetherYes — encrypts the key bag and MACs the archiveBinary DER
P7B / P7C (PKCS#7)Certificates only — never a keyNoneBinary DER or -----BEGIN PKCS7-----
PEM filesAnything, one block each — usually separate cert, chain and key filesOnly if the key itself is encryptedBase64 text

The certificate formats guide covers DER and JKS; here we stay inside the archive.

Who asks for one, and why

Windows and IIS. The certificate store imports a private key only through PKCS#12, and IIS offers a certificate in a binding only if it sits in Local Computer → Personal with its key attached. Azure.App Service’s documented requirements for an uploaded private certificate are a password-protected PFX encrypted with triple DES, a key of at least 2048 bits and the whole chain including the root; Key Vault imports PFX or PEM and hands an exportable certificate back as a passwordless PFX. Java. Since Java 9 (JEP 229) keytool creates PKCS#12 keystores by default even when the file is named .jks; a PKCS#12 keystore cannot have separate store and key passwords (keytool warns Different store and key passwords not supported for PKCS12 KeyStores) — see the Java keystore guide. Code signing and client identities. signtool sign /f code.pfx /p password … is the classic Authenticode call, though since June 2023 the CA/Browser Forum requires publicly trusted code-signing keys on hardware, so a PFX now appears only for older or private-CA signing certificates. Mutual TLS, EAP-TLS Wi-Fi and VPN clients import the same file as a user identity.

What the password protects — and the two encryption generations

The password does two jobs: it derives the key that encrypts the shrouded key bag, and the key for the MAC over the whole AuthenticatedSafe, which is why an importer can reject a wrong password before decrypting anything. Both start from the password encoded as a BMPString — UTF-16 with a two-byte terminator — so keep PFX passwords to plain ASCII. Even an empty password has two encodings, an empty BMPString (what openssl pkcs12 -export -passout pass:writes) or no password at all; a reader must try both, and the site’s tools do before ever showing a password box.

The algorithms changed in 2021, the root of most modern PFX trouble. openssl pkcs12 -info -in site.pfx -noout shows which generation you hold:

# Legacy (OpenSSL 1.x default, Windows "TripleDES-SHA1")
MAC: sha1, Iteration 2048
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048

# Modern (OpenSSL 3 default, Windows "AES256-SHA256")
MAC: sha256, Iteration 2048
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256

OpenSSL 3.0 moved its defaults from RC2-40 (certificates) and 3DES (key) with an SHA-1 MAC to AES-256-CBC under PBKDF2 with an SHA-256 MAC, adding -legacyto write the old form. Older Windows and Java releases do not understand PBES2 or an SHA-256 MAC, and the symptom is not an “unsupported algorithm” message but the ordinary “password is incorrect” dialog. The reverse trap is newer: RC2 lives in OpenSSL 3’s optional legacy provider, so a build without it cannot read a file it wrote with -legacyError outputting keys and certificates digital envelope routines::unsupported … (RC2-40-CBC : 0) — until you add -legacy to the read as well.

When we built the packers on this site we deliberately sat between the generations. A file from PEM to PFX, P7B to PFX or the password changer reports Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048 and MAC: sha1, Iteration 2048, with the certificates stored in the clear (PKCS7 Data, what -certpbe NONE produces): no RC2, so OpenSSL 3 opens it without -legacy; 3DES, so every Windows and Java release opens it too. The readers are broader — we checked them against archives from OpenSSL 1.x, OpenSSL 3 and the packers themselves.

The four round trips

PFX → PEM

Open or drop the .pfx/.p12 on PFX to PEM. It tries the empty and absent passwords silently and asks only if the file genuinely needs one. A chip lists the common names inside in archive order, and up to three downloads appear: certificate.pem (the first certificate), ca-chain.pem (the rest, if any) and private-key.pem (if a key is present). The key downloads unencrypted — BEGIN RSA PRIVATE KEY for RSA, BEGIN PRIVATE KEY for EC — so treat that file like the password itself.

PEM + key (+ chain) → PFX

PEM to PFX has a certificate box and a key box; dropped .pem/.crt/.key files are routed by their BEGIN line and certificates append, so fullchain.pemis the natural input. The leaf must come first: the tool derives the public key from your private key and compares it byte for byte with the first certificate’s, failing with The private key does not match the first certificate — make sure the leaf (domain) certificate comes first and the key belongs to it. It accepts PKCS#8, PKCS#1 RSA and SEC1 EC keys on P-256, P-384 or P-521, decrypting encrypted keys with the passphrase you type in the second field. A password for the new file is required, the .pfx/.p12 radio picks the download name, and the leaf’s CN becomes the friendlyName with its SHA-1 as localKeyID — what Windows expects.

P7B + key → PFX

A Windows CA hands back a .p7b with your certificate and chain but no key, and the target wants a PFX. P7B to PFX opens the bundle in binary or BEGIN PKCS7 form, reports bundle.p7b — 3 certificates, then does the one thing OpenSSL will not: compares your key against every certificate in the bundle, puts the match first and packs the rest as the chain. No match gives The private key does not match any certificate in this .p7b — check you have the right key. The right key was generated with the CSR and never left that machine; the CSR guide explains why.

Change or remove the password

The OpenSSL route is unpack-then-repack: openssl pkcs12 -in old.pfx -nodes -out all.pem, then openssl pkcs12 -export -in all.pem -out new.pfx, leaving all.pem on disk with the key in the clear until you delete it (OpenSSL 3.6 refuses to read the export side from a pipe — Could not find certificates from -in file from <stdin>). Change PFX password does the repack in memory: open the file, unlock it, type a new password or tick remove the password (blank), and download certificate.pfx. Certificates and key pass through byte-identical; only the wrapper is rebuilt — with the 3DES/SHA-1 scheme above, so the same step turns an OpenSSL 3 file an old Windows rejects into one it accepts. Two limits: only the first private key in a multi-key archive is kept, and a certificate-only file is refused with This file holds certificates but no private key — nothing to re-password.

OpenSSL reference

TaskCommandNotes
Inspect encryption and contentsopenssl pkcs12 -info -in site.pfx -nooutMAC and PBE algorithms, bag attributes, subjects
PEM → PFXopenssl pkcs12 -export -out site.pfx -inkey privkey.pem -in fullchain.pem -name site-certfile chain.pem if the chain is separate; -legacy for old importers
All certificates outopenssl pkcs12 -in site.pfx -nokeys -out certs.pemAdd -clcerts for the leaf only, -cacerts for the chain only
Key out, unencryptedopenssl pkcs12 -in site.pfx -nocerts -nodes -out key.pemOpenSSL 3 also spells it -noenc; omit it for an encrypted PEM key
P7B → PEM certificatesopenssl pkcs7 -print_certs -in bundle.p7b -out certs.pem-inform DER for a binary bundle, then -export as above

Reading the error you got

Mac verify error: invalid password? — OpenSSL, wrong password. Check keyboard layout, trailing spaces and non-ASCII characters, then try the empty password with -passin pass:. The site’s tools report the same as Wrong password for this .pfx file.

The password you entered is incorrect. — the Windows import wizard: a genuine mismatch or, for a file from OpenSSL 3 or an AES256-SHA256 export, a generation this Windows cannot read. PBES2 or MAC: sha256 in -info means re-export with -legacy or repack through the password changer. Java’s java.io.IOException: keystore password was incorrect has the same two causes.

No cert in -in file 'cert.pem' matches private key (older builds: No certificate matches private key) — -export was given a key from a different pair; the certificate ↔ key matcher derives the public key from each item and names the odd one out. Could not find private key from -inkey file (older: unable to load private key) means the -inkey file has no PRIVATE KEY block.

Could not read the file: … from the site’s PFX tools means the bytes are not PKCS#12 — typically a P7B or PEM renamed to .pfx. To see what a PFX contains without extracting anything, load it on the certificate checker, which tries a blank password first and reads only the certificates, leaving the key sealed.

Common mistakes

  • Exporting without the chain. Skip the Windows wizard’s Include all certificates in the certification path if possible box and the file holds the leaf alone; IIS on the destination then serves an incomplete chain that desktop browsers hide and phones and curl expose. Rebuild the chain with the chain builder and repack — the incomplete-chain guide has the symptoms.
  • Importing into the wrong store. A PFX belongs in Personal, not Trusted Root Certification Authorities, and for IIS in Local Computer (certlm.msc), not Current User (certmgr.msc). Get either wrong and the binding dropdown stays empty while the import reports success.
  • A key never marked exportable. If the original import did not tick Mark this key as exportable, the export wizard greys out Yes, export the private key and you get a certificate-only PFX. No converter can recover the key; re-issue from a CSR on a machine where it is exportable.
  • Leaf not first. The packers verify the key against the first certificate and the unpacker treats the first bag as certificate.pem; if the CN chip shows the intermediate first, reorder the PEM.
  • An encrypted SEC1 EC key. A BEGIN EC PRIVATE KEY block with Proc-Type: 4,ENCRYPTED is the one key form the packers refuse: Encrypted SEC1 EC keys are not supported — decrypt it first: openssl ec -in key.pem -out key-plain.pem.

Handling and hygiene

A PFX and its password together are the complete identity: whoever holds both can impersonate the server or user until the certificate expires or is revoked. Never send them in the same message or channel — file by one route, password by another — and prefer a short-lived direct transfer over a shared drive that keeps copies. When a vendor ships a throwaway password, re-password the file before it goes anywhere, with a passphrase of the kind the passwords guide recommends; both encryption generations use 2048 iterations, which is little brake on a dictionary attack. Delete the archive once the import succeeds, and give an unpacked private-key.pem chmod 600 and no spare copies. Because the archive contains the key, unpack it only where the bytes stay on your machine — which is why every tool linked here works in the browser tab and offline.

Do this

  • Run openssl pkcs12 -info -in file.pfx -noout (or open the file on the certificate checker) first: it shows the encryption generation and whether a key is present.
  • Unpack with PFX to PEM for Nginx or Apache; pack with PEM to PFX or P7B to PFX for Windows, IIS, Azure and Java, leaf first with the full chain.
  • When an older Windows or Java says the password is wrong and -info shows PBES2, re-export with -legacy or repack through the password changer.
  • Keep the file and its password on separate channels, re-password anything that arrived with a throwaway password, and delete the archive after import.

Frequently asked questions

Is a .pfx file the same as a .p12 file?

Yes. Both are PKCS#12 archives with identical bytes inside; .pfx is the extension Windows and IIS favour, .p12 the one Java, macOS and iOS favour. Rename freely — every importer checks the content, not the name.

Can I open a PFX file without the password?

Not the private key. The password derives both the key that encrypts the key bag and the MAC that guards the whole archive, and there is no recovery route. Many exporters leave the certificates themselves unencrypted, so the public half can sometimes be read, but the key cannot.

Why does Windows say the password is incorrect when I know it is right?

Either a typing problem — keyboard layout, trailing space, a non-ASCII character — or the file uses the AES-256 / SHA-256 encryption that OpenSSL 3 writes by default and an older Windows cannot read. Run openssl pkcs12 -info on it; if you see PBES2 and sha256, re-export with -legacy or re-password it with the site's tool, which writes 3DES.

Does a PFX file always contain the private key?

No. Windows lets you export with "No, do not export the private key", and some CA portals ship certificate-only PKCS#12 files. The PFX to PEM tool shows no private-key.pem button for those, and the password changer refuses them because there is nothing to protect.

How do I convert a PFX to PEM for Nginx or Apache?

Open the .pfx in the PFX to PEM tool, enter the password if asked, and download certificate.pem, ca-chain.pem and private-key.pem. Concatenate the first two (leaf first) for Nginx's ssl_certificate and point ssl_certificate_key at the key. With OpenSSL: openssl pkcs12 -in site.pfx -nokeys -out certs.pem and openssl pkcs12 -in site.pfx -nocerts -nodes -out key.pem.

Tools used in this guide

Every one of these runs in your browser — the files you work on never leave your device.

More certificates & keys guides