OpenSSL 3 cannot read my PFX
If OpenSSL 3 fails to open a `.pfx` or `.p12` with a MAC error, an "unsupported" decoder message, or an apparent wrong-password error, the cause is almost always the algorithm rather than the password. OpenSSL 3 moved RC2, 3DES and other legacy ciphers into a provider that is not loaded by default, and older Windows and Java tools wrote archives using exactly those. Add `-legacy` to the command — `openssl pkcs12 -legacy -in certificate.pfx -out certificate.pem -nodes`. The same flag on export produces archives those older tools can read.
What actually changed #
OpenSSL 3.0 reorganised algorithms into providers. Ciphers considered obsolete — RC2, RC4,
3DES-in-PKCS#12, MD2, and others — moved into a legacy provider that is not loaded by default.
At the same time, the default PKCS#12 export encryption became AES-256-CBC with PBKDF2 and a SHA-256 MAC.
The result is a two-way incompatibility:
| Archive written by | Read by OpenSSL 3 | Read by older tools |
|---|---|---|
| OpenSSL 1.x, older Windows, older Java | Fails without -legacy |
Fine |
| OpenSSL 3 defaults | Fine | Fails, usually reported as a bad password |
Reading an old archive #
openssl pkcs12 -legacy -in certificate.pfx -info -noout If that works where the command without -legacy failed, you have confirmed the diagnosis. Then
extract as normal:
openssl pkcs12 -legacy -in certificate.pfx -nocerts -nodes -out private.key
openssl pkcs12 -legacy -in certificate.pfx -clcerts -nokeys -out certificate.crt Writing an archive an old tool can read #
openssl pkcs12 -export -legacy \
-out certificate.pfx \
-inkey private.key -in certificate.crt -certfile chain.crt If your build does not accept -legacy, name the algorithms explicitly:
openssl pkcs12 -export \
-certpbe PBE-SHA1-3DES \
-keypbe PBE-SHA1-3DES \
-macalg sha1 \
-out certificate.pfx \
-inkey private.key -in certificate.crt -certfile chain.crt Telling which encryption an archive uses #
openssl pkcs12 -in certificate.pfx -info -noout 2>&1 | head -n 5 Expected output
MAC: sha256, Iteration 2048 PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
AES-256-CBC and a sha256 MAC mean a modern archive. RC2-40-CBC or 3DES with a sha1 MAC
mean a legacy one.
The better long-term fix #
-legacy is a compatibility shim, not a resting place. Where you can:
- Upgrade the consuming system. Windows Server 2019 and later, and JDK 8u301 and later, read AES-256 PKCS#12 archives without complaint.
- Avoid PKCS#12 for transport. If both ends are Unix, moving separate PEM files sidesteps the entire problem.
- Re-protect at the destination. If you must use
-legacyto move a key, do not leave it in that archive afterwards.
Loading the provider globally (usually a mistake) #
It can be done through openssl.cnf:
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1 Frequently asked questions
- Does -legacy weaken the file?
- Yes. It selects RC2 or 3DES with a SHA-1 MAC, all of which are deprecated. Use it only when the system on the other end cannot read a modern archive, treat the file as short-lived transport, and protect the key properly at the destination.
- Why does Windows say the password is wrong when it is correct?
- Because the older Windows importer cannot decrypt an AES-256-CBC archive, and it reports every decryption failure as a bad password. The symptom points at the wrong cause, which is why this wastes so much time.
- Which OpenSSL versions are affected?
- OpenSSL 3.0 and later. OpenSSL 1.1.1 and earlier used RC2/3DES by default and had no separate legacy provider, so they read and write the old format without any flag.
- Can I load the legacy provider permanently?
- Yes, by adding a legacy provider section to openssl.cnf, but it is better not to. A per-command
-legacyflag keeps the weak algorithms opt-in rather than making every operation on the machine quietly accept them.
References
- RFC 7292: PKCS #12 — the archive format, including the MAC OpenSSL 3 rejects on legacy files
- openssl-pkcs12(1) — -legacy and the provider it loads