# PKCS#12 — .pfx and .p12

PKCS#12 is a password-protected binary archive that bundles a certificate, its private key and the intermediate chain into a single file. `.pfx` and `.p12` are the same format under different extensions — Microsoft tooling writes `.pfx` and most other software writes `.p12`. It is the standard way to move a complete TLS identity between systems, and the format most likely to cause trouble across OpenSSL versions, because OpenSSL 3 changed the default encryption to AES-256-CBC and older importers cannot read it.

Source: https://certguru.io/formats/pkcs12-pfx-p12/
Last updated: 2026-08-19
Commands verified against OpenSSL 3.5.7.

---

## What is inside

A PKCS#12 archive is a set of "bags", each holding one object:

| Bag type | Contents |
| --- | --- |
| Certificate bag | One certificate. A typical archive has one per chain member. |
| Shrouded key bag | The private key, encrypted with a key derived from the password. |
| Bag attributes | The friendly name and local key ID that associate a key with its certificate. |

The whole file is then MAC-protected using the same password, so tampering is detectable.

*List the bags without extracting anything*

```bash
openssl pkcs12 -in certificate.pfx -info -noout
```

Expected output:

```
MAC: sha256, Iteration 2048
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC
Certificate bag
Certificate bag
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC
```

Two certificate bags plus one shrouded key bag is the shape of a normal server identity with one
intermediate. No shrouded key bag means there is no private key in the file.

## The OpenSSL 3 problem

This is the single most common thing to go wrong with PKCS#12 files, and it is worth understanding
rather than just memorising the flag.

OpenSSL 3.0 changed the default PKCS#12 encryption from RC2/3DES with a SHA-1 MAC to **AES-256-CBC
with PBKDF2 and a SHA-256 MAC**. That is unambiguously the right default. But implementations
written against the original specification — older Windows CryptoAPI, older JDKs, various
appliances — do not implement those algorithms, and when decryption fails they report it as an
incorrect password rather than an unsupported cipher. So the symptom points at the wrong cause.

*Export something an old importer can read*

```bash
openssl pkcs12 -export -legacy \
  -out certificate.pfx -inkey private.key -in certificate.crt
```

*Read an archive an old tool produced*

```bash
openssl pkcs12 -legacy -in certificate.pfx -info -noout
```

> **-legacy means weak algorithms**
>
> RC2, 3DES and SHA-1 are all deprecated for good reasons. Use `-legacy` only when the system on the
>   other end genuinely cannot read a modern archive, treat the file as short-lived transport, and
>   protect the key properly once it arrives.

## Creating and unpacking

*PEM files in, one archive out*

```bash
openssl pkcs12 -export \
  -out certificate.pfx \
  -inkey private.key \
  -in certificate.crt \
  -certfile chain.crt \
  -name "example.com"
```

*Archive in, PEM files out*

```bash
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out private.key
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out certificate.crt
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -out chain.crt
```

Full detail at [PEM to PFX](/convert/pem-to-pfx/) and [PFX to PEM](/convert/pfx-to-pem/).

## Where you will meet it

- **IIS and Windows** use PKCS#12 as the native import and export format for TLS identities.
- **Java 9 and later** default to PKCS#12 for keystores, replacing
  [JKS](/formats/jks-keystore/).
- **Azure, AWS and most managed platforms** accept a PFX upload as the way to supply a certificate
  with its key.
- **Moving a certificate between machines** — it is the one file format that carries the complete
  identity, which is exactly why it needs a password.

## Handling it safely

> **A PFX is a private key in a wrapper**
>
> Anyone with the file and the password has your identity. The password is the only thing protecting
>   it, so it should be strong and it should not travel alongside the file. Delete the archive once
>   imported — a `.pfx` left in a downloads directory or an email attachment is a credential sitting
>   in the clear-ish.

Setting a blank password with `-passout pass:` produces a file with no protection at all. Some
import tools require it, most refuse it, and it should never touch a shared filesystem.


## Frequently asked questions

### What is the difference between .pfx and .p12?

Nothing. The binary format is identical and every tool reads either. `.pfx` came from Microsoft's earlier PFX specification, which PKCS#12 superseded, and the name stuck in the Windows ecosystem.

### Does a PFX always contain the private key?

Almost always, and that is the point of the format — but not necessarily. An archive exported with `-nokeys` holds certificates only. Run `openssl pkcs12 -info -noout` and look for a Shrouded Keybag to find out.

### Why does Windows say my password is wrong when it is not?

Because OpenSSL 3 encrypted the archive with AES-256-CBC and the older Windows importer cannot decrypt it, reporting the failure as a bad password. Re-export with `-legacy`. See the full explanation.

### Can a PFX hold more than one certificate and key?

Yes. The format is a general bag structure and can hold several identities, each distinguished by a friendly name. In practice almost every PFX holds exactly one identity plus its chain.


## References

- [RFC 7292: PKCS #12](https://www.rfc-editor.org/rfc/rfc7292) — the archive format, including the MAC OpenSSL 3 rejects on legacy files

## Related pages

- https://certguru.io/convert/pem-to-pfx/
- https://certguru.io/convert/pfx-to-pem/
- https://certguru.io/errors/unsupported-mac-pkcs12-legacy/
- https://certguru.io/formats/jks-keystore/
- https://certguru.io/platforms/iis/
- https://certguru.io/openssl/extract-cert-from-pkcs12/

--
CertGuru — https://certguru.io
