# OpenSSL PKCS#12 (.pfx) commands and the 3.0 -legacy change

> Create, inspect, split and repair PKCS#12/PFX files with OpenSSL 3.x: the AES-256 default since 3.0, -legacy for old Windows and Java, common errors.

Source: https://ssl.sh/cookbook/pkcs12/
Last updated: 2026-11-10

`openssl pkcs12` creates and reads [PKCS#12](https://ssl.sh/glossary/pkcs12/) (`.pfx`, `.p12`) files, which hold a private key, its certificate and the chain under one password. The main pitfall is OpenSSL 3.0: new files use AES-256 and an SHA-256 MAC that older Windows, macOS and Java may reject, and old RC2 files need `-legacy` to open. Outputs are from OpenSSL 3.6.1.

For plain format conversions (PFX to PEM, PFX to JKS) see [convert formats](https://ssl.sh/cookbook/convert-formats/), or use the in-browser [SSL converter](https://ssl.sh/tools/ssl-converter/).

## Create a PFX with the key, certificate and chain

`-inkey` is the key, `-in` the leaf certificate, `-certfile` the intermediates and `-name` the friendly name shown on import.

```bash
openssl pkcs12 -export -inkey server.key -in server.crt -certfile intermediate.crt \
  -name "example.com" -out example.pfx
```

```text
Enter Export Password:
Verifying - Enter Export Password:
```

The friendly name becomes the alias in Java keystores and the label in the Windows certificate store. If the key does not belong to the certificate, OpenSSL stops with `No cert in -in file 'server.crt' matches private key`. You can also pass a single `fullchain.pem` with `-in`; OpenSSL picks the certificate matching the key as the leaf.

## Let OpenSSL build the chain

`-chain` builds the path from the leaf to a trusted root and includes every certificate on it.

```bash
openssl pkcs12 -export -inkey server.key -in server.crt -chain \
  -untrusted intermediate.crt -CAfile root.crt -name example.com -out example.pfx
openssl pkcs12 -in example.pfx -nokeys -passin pass:changeit | grep subject=
```

```text
subject=CN=example.com
subject=CN=Example Test Intermediate CA
subject=CN=Example Test Root CA
```

Without a usable root, `-chain` fails with `Error getting chain: unable to get local issuer certificate`. For public certificates, the system trust store is used when you omit `-CAfile`. `-chain` also adds the root, which servers do not need to send.

## Check which algorithms a PFX uses

`-info -noout` prints the MAC and encryption algorithms without writing any keys.

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

```text
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 16
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
Certificate bag
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
```

This is the OpenSSL 3.x default. A legacy file shows `MAC: sha1` and `pbeWithSHA1And40BitRC2-CBC` or `pbeWithSHA1And3-KeyTripleDES-CBC` instead. Run this first whenever an import fails: it tells you which side needs to change.

## Understand the OpenSSL 3.0 default change

OpenSSL 3.0 switched PKCS#12 export from legacy PKCS#12 PBE algorithms to PBES2 with AES-256.

| | OpenSSL 1.1.1 and older | OpenSSL 3.0 and later (default) | 3.x with `-legacy` |
|---|---|---|---|
| Certificate encryption | RC2-40-CBC | AES-256-CBC, PBKDF2, HMAC-SHA256 | RC2-40-CBC (or 3DES if RC2 is disabled) |
| Private key encryption | 3DES | AES-256-CBC, PBKDF2, HMAC-SHA256 | 3DES |
| MAC | SHA-1 | SHA-256 | SHA-1 |
| Iterations | 2048 | 2048 | 2048 |

RC2 and other old ciphers moved into the legacy provider in 3.0, which is not loaded by default. That is why the same command that worked for years now produces files some importers refuse, and why 3.x cannot open files made by older tools until you add `-legacy`.

## Create a PFX for old Windows, macOS or Java

Add `-legacy` to get the pre-3.0 algorithms.

```bash
openssl pkcs12 -export -legacy -inkey server.key -in server.crt -certfile intermediate.crt \
  -name example.com -out example-legacy.pfx
openssl pkcs12 -in example-legacy.pfx -info -noout -legacy
```

```text
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 16
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
Certificate bag
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
```

Use it when Windows Server 2016 or older, an old macOS Keychain, older Java 8 builds, or network appliances report a wrong password for a file you know is correct. If OpenSSL was installed outside the system paths, `-legacy` may also need `-provider-path` or the `OPENSSL_MODULES` environment variable to find `legacy.so`.

## Choose the algorithms explicitly

`-certpbe`, `-keypbe` and `-macalg` select algorithms without loading the legacy provider.

```bash
openssl pkcs12 -export -inkey server.key -in server.crt -certfile intermediate.crt \
  -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 \
  -name example.com -out example-compat.pfx
openssl pkcs12 -in example-compat.pfx -info -noout
```

```text
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 16
PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
Certificate bag
Certificate bag
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
```

3DES is still in the default provider, so this file is readable by OpenSSL 3.x without `-legacy` and by nearly every older importer. It is the best compromise when you do not know what will read the file. `-iter 100000` raises the iteration count to slow down password guessing.

## Read a legacy PFX on OpenSSL 3.x

An `unsupported` error naming `RC2-40-CBC` means the file uses legacy algorithms; add `-legacy`.

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

```text
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 16
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Error outputting keys and certificates
...:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:...:Algorithm (RC2-40-CBC : 0), Properties ()

MAC: sha1, Iteration 2048
...
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
```

The same `0308010C ... unsupported` error appears in Node.js 17+, Python and other software linked against OpenSSL 3. To fix it permanently, re-export once with `-legacy` on input and default (AES) on output, as shown in "Change the password" below.

## Fix "Mac verify error: invalid password?"

This message means the MAC did not verify: almost always a wrong password, occasionally an unsupported MAC.

```bash
openssl pkcs12 -in example.pfx -noout -passin pass:wrong
```

```text
Mac verify error: invalid password?
```

Libraries report the same condition as `mac verify failure`. Check for an empty password (`-passin pass:`), a trailing newline in a password file, or non-ASCII characters that the exporting tool encoded differently. If the password is definitely right and the file came from a very new system, an older reader may not know the MAC algorithm (for example PBMAC1, which OpenSSL 3.4 and later can write with `-pbmac1_pbkdf2`).

## Extract the key, certificate and CA certificates

`-nocerts`, `-clcerts -nokeys` and `-cacerts -nokeys` separate the parts.

```bash
openssl pkcs12 -in example.pfx -nocerts -noenc -out example.com.key
openssl pkcs12 -in example.pfx -clcerts -nokeys -out example.com.crt
openssl pkcs12 -in example.pfx -cacerts -nokeys -out chain.crt
head -3 example.com.key
```

```text
Bag Attributes
    localKeyID: 12 21 0D 64 22 E9 E3 EB CC 6B 65 BE B6 A8 CA 17 5F 63 39 3A 
    friendlyName: example.com
```

`-clcerts` outputs only the certificate that matches the key, which avoids picking the wrong one from an unordered file. Without `-noenc` the key is re-encrypted with a new PEM passphrase. Add `-legacy` to each command for legacy files.

## Create a certificate-only PKCS#12 truststore

Use `-nokeys` to package CA certificates for Java or other clients that need a truststore.

```bash
openssl pkcs12 -export -nokeys -in root.crt -jdktrust anyExtendedKeyUsage \
  -out truststore.p12
openssl pkcs12 -in truststore.p12 -nokeys | head -2
```

```text
Bag Attributes
    Trusted key usage (Oracle): Any Extended Key Usage (2.5.29.37.0)
```

Java only treats a certificate in a PKCS#12 store as a trusted entry when it carries Oracle's trusted-key-usage attribute, which `-jdktrust` (OpenSSL 3.2 and later) adds. On older OpenSSL versions build the truststore with `keytool -importcert` instead.

## Create a PFX without a password

An empty `-passout pass:` still encrypts with an empty password; `-keypbe NONE -certpbe NONE -nomac` stores everything in clear.

```bash
openssl pkcs12 -export -inkey server.key -in server.crt -passout pass: -out nopass.pfx
openssl pkcs12 -export -inkey server.key -in server.crt \
  -keypbe NONE -certpbe NONE -nomac -passout pass: -out plain.pfx
openssl pkcs12 -in plain.pfx -info -noout -passin pass:
```

```text
Warning: MAC is absent!
PKCS7 Data
Certificate bag
PKCS7 Data
Key bag
```

Some software (for example certain container platforms) expects a password-less PFX. The unencrypted variant is a plain private key in a different wrapper, so protect it like a `.key` file. `-nomac` is also used with the FIPS provider, which cannot compute the PKCS#12 MAC.

## Change the password of a PFX

Export to a temporary PEM, re-export with the new password, then delete the temporary file.

```bash
umask 077
openssl pkcs12 -in example.pfx -noenc -passin file:old.txt -out tmp-all.pem
openssl pkcs12 -export -in tmp-all.pem -name example.com -passout file:new.txt -out example-new.pfx
rm -f tmp-all.pem
```

```text
(no output on success)
```

Piping the first command into the second does not work, because `pkcs12 -export` reads its input twice and stdin can only be read once (`Could not find certificates from -in file from <stdin>`). Add `-legacy` to the first command to upgrade an old RC2 file to AES at the same time.

## Run pkcs12 in scripts without prompts

Pass passwords through `file:`, `env:` or `fd:` instead of typing them.

```bash
P12PASS='s3cret' openssl pkcs12 -in example.pfx -noout -passin env:P12PASS && echo ok
openssl pkcs12 -export -inkey server.key -in server.crt -passout file:pfx-pass.txt -out example.pfx
```

```text
ok
```

`pass:secret` works too but shows up in `ps` output and shell history. `-passin` is the password for reading a PFX (or the input key during `-export`); `-passout` is the password for the file being written. See [troubleshooting](https://ssl.sh/cookbook/troubleshooting/) for more OpenSSL error messages.
