How to Extract a Private Key from PFX with OpenSSL

Use OpenSSL pkcs12 to extract a private key, certificate, and CA chain from a PFX or P12 file on Linux. Covers password-protected and empty-password PFX, unencrypted PEM for nginx, -legacy for old Windows exports, and verification steps with real command output.

Published

Updated

Read time 7 min read

Reviewed byDeepak Prasad

How to Extract a Private Key from PFX with OpenSSL

You exported a .pfx or .p12 file from Windows IIS, Azure, or a vendor portal and now Linux wants separate PEM files—a private key and a certificate nginx or Apache can load. OpenSSL’s pkcs12 subcommand unpacks PKCS#12 bundles: one import password unlocks the PFX, then you split out the key, leaf certificate, and CA chain.

This guide covers every common extraction path: private key with password, unencrypted key for nginx (-nodes), PFX files that use an empty import password, certificate and chain only, legacy Windows PFX that need -legacy, and how to verify the key matches the cert. I ran the commands on Ubuntu 25.04 with OpenSSL 3.4.1 using a test PFX built from a local certificate. For creating PFX files from scratch, see Generate a self-signed certificate.

Tested on: Ubuntu 25.04 (Plucky Puffin); kernel 6.14.0-37-generic; OpenSSL 3.4.1.


Quick answer: extract private key from PFX

Replace cert.pfx and the password with your file and export password:

bash
openssl pkcs12 -in cert.pfx -passin pass:YOUR_PFX_PASSWORD \
  -nocerts -nodes -out private.key
  • -passin — password that protects the PFX file (the export/import password)
  • -nocerts — extract only the private key, not certificates
  • -nodes — write an unencrypted PEM key (required for nginx/Apache auto-reload)

Confirm the key is valid:

bash
openssl rsa -in private.key -noout -check
text
RSA key ok

The PEM file should contain -----BEGIN PRIVATE KEY----- (or BEGIN RSA PRIVATE KEY). A few lines of Bag Attributes above that header are normal and nginx ignores them.


PFX, P12, and PKCS#12 — same format

Extension Same thing? Typical source
.pfx Yes Windows IIS, MMC, Azure App Service
.p12 Yes Java keystores, macOS Keychain export
PKCS#12 Format name Bundles private key + cert(s) + optional CA chain

One PFX file can hold the end-entity certificate, intermediate CAs, and the matching private key. The PFX password (export password) encrypts the whole bundle—it is not the same as a passphrase on an extracted .key unless you choose to encrypt the output.

Inspect what is inside without extracting:

bash
openssl pkcs12 -in cert.pfx -passin pass:YOUR_PFX_PASSWORD -info -noout
text
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
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

Two certificate bags usually mean leaf plus CA/intermediate; Shrouded Keybag confirms a private key is present.


Extract private key with PFX password

Unencrypted key for nginx or Apache (most common)

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -nocerts -nodes -out server.key
chmod 600 server.key

OpenSSL 3.x accepts -noenc as an alias for -nodes on extract.

Encrypted output key (keep passphrase on the PEM)

Omit -nodes. OpenSSL prompts twice for a new password that encrypts server.key:

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -nocerts -out server-encrypted.key

Or set the output password on the command line:

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -nocerts -passout pass:KeyPass456 -out server-encrypted.key

Decrypt later for nginx:

bash
openssl rsa -in server-encrypted.key -passin pass:KeyPass456 -out server.key

Pass PFX password without shell history

bash
read -s PFXPASS
openssl pkcs12 -in server.pfx -passin env:PFXPASS -nocerts -nodes -out server.key
unset PFXPASS

From a file (restrict permissions):

bash
chmod 600 pfx.pass
openssl pkcs12 -in server.pfx -passin file:pfx.pass -nocerts -nodes -out server.key
shred -u pfx.pass

Extract private key when the PFX has no password

Some lab exports or internal tools create a PFX with an empty import password. Use -passin pass: (nothing after the colon):

bash
openssl pkcs12 -in nopass.pfx -passin pass: -nocerts -nodes -out private.key

If OpenSSL still fails, the file is not passwordless—try the export password from the team that generated the PFX.

WARNING
An empty PFX password is weak. After extraction, protect private.key with chmod 600 and store the PFX only on encrypted media.

Extract certificate and CA chain (not the key)

Leaf / entity certificate only (-clcerts):

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -clcerts -nokeys -out server.crt
text
subject=CN=nginx.example.test

CA / intermediate certificates (-cacerts):

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -cacerts -nokeys -out ca-chain.crt

Full chain for nginx (leaf first, then intermediates):

bash
cat server.crt ca-chain.crt > fullchain.crt

nginx example:

nginx
ssl_certificate     /etc/nginx/ssl/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;

See Install SSL certificate on Nginx for the full server block.


Extract everything into one PEM file

When you want a single file with key and certs (some tools accept combined PEM):

bash
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
  -nodes -out bundle.pem

The file contains multiple BEGIN blocks (key plus one or more certificates). Split later with an editor, or use the separate -nocerts / -nokeys commands above for cleaner nginx layout.


Windows PFX on Linux: -legacy

Older Windows exports sometimes use RC2 or legacy 3DES inside the PFX. OpenSSL 3.x defaults to modern ciphers; if you see errors like unsupported or digital envelope routines, add -legacy:

bash
openssl pkcs12 -in legacy.pfx -passin pass:Legacy123 -legacy \
  -nocerts -nodes -out private.key

The same flag works when creating PFX files for older Windows tools:

bash
openssl pkcs12 -export -in cert.crt -inkey private.key -out legacy.pfx \
  -passout pass:Legacy123 -legacy

Verify key matches certificate

After extraction, confirm the private key belongs to the certificate before you reload a web server:

bash
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa  -noout -modulus -in server.key  | openssl md5

Matching MD5(stdin)= lines mean the pair is correct. For full cert details, see View certificate with OpenSSL.

Test TLS once nginx is configured:

bash
openssl s_client -connect your.host.name:443 -servername your.host.name </dev/null 2>/dev/null | \
  openssl x509 -noout -subject -dates

End-to-end script for nginx

Adjust paths and password handling for production (avoid pass: in scripts when possible):

bash
PFX=server.pfx
PFXPASS='ExportPass123'

openssl pkcs12 -in "$PFX" -passin pass:"$PFXPASS" -nocerts -nodes -out server.key
openssl pkcs12 -in "$PFX" -passin pass:"$PFXPASS" -clcerts -nokeys -out server.crt
openssl pkcs12 -in "$PFX" -passin pass:"$PFXPASS" -cacerts -nokeys -out chain.crt
cat server.crt chain.crt > fullchain.crt
chmod 600 server.key

Move files to /etc/nginx/ssl/ or your distro layout, then nginx -t && systemctl reload nginx.


Troubleshooting

Error or symptom Cause Fix
Mac verify error: invalid password? Wrong PFX import password Use the export password from IIS/Azure; check caps lock
No BEGIN PRIVATE KEY in output Missing -nodes or no key in PFX Add -nocerts -nodes; re-export PFX with private key included
unsupported / algorithm error Legacy PFX cipher Add -legacy
nginx rejects the key file Encrypted PEM or wrong format Re-extract with -nodes; ensure RSA/EC PEM headers
Empty chain.crt No CA certs in PFX Download intermediate from CA site; append to fullchain.crt
Bag Attributes only in file Opened wrong output or incomplete extract Use -nocerts -nodes; look for BEGIN PRIVATE KEY below attributes

Wrong password example from a failed run:

text
Mac verify error: invalid password?

Security notes

  • The PFX password protects the bundle at rest; -nodes output is plaintext—restrict with filesystem permissions and avoid committing keys to git.
  • Delete temporary PFX copies on shared servers after extraction.
  • Prefer extracting on the target Linux host rather than emailing PFX files.
  • Public websites should use CA-issued certificates, not long-lived self-signed material from a PFX lab export.

References


Summary

Use openssl pkcs12 -in cert.pfx -passin pass:PFX_PASSWORD -nocerts -nodes -out private.key to extract an unencrypted private key for nginx or Apache. The -passin value is always the PFX import password; -nodes controls whether the output key is encrypted. Split certificates with -clcerts -nokeys for the leaf and -cacerts -nokeys for the chain, then concatenate for ssl_certificate. For passwordless PFX files, use -passin pass:. Old Windows exports may need -legacy on OpenSSL 3.x. Verify with openssl rsa -check and matching modulus MD5 hashes before you reload a web server.

Frequently Asked Questions

1. What is the OpenSSL command to extract a private key from a PFX file?

Run openssl pkcs12 -in cert.pfx -passin pass:YOUR_PFX_PASSWORD -nocerts -nodes -out private.key. The -nocerts flag skips certificates; -nodes writes an unencrypted PEM key nginx and Apache can load without prompting.

2. How do I extract a private key from PFX without a password on the output key?

Add -nodes (or -noenc on OpenSSL 3.x) so OpenSSL does not encrypt the extracted PEM. You still need -passin with the PFX import password unless the PFX itself was created with an empty password (-passin pass:).

3. What if my PFX file has no import password?

Use -passin pass: with an empty password: openssl pkcs12 -in cert.pfx -passin pass: -nocerts -nodes -out private.key. Some Windows exports use a blank password; others always require the export password you set in IIS or certmgr.

4. How do I extract the certificate and chain from a PFX for nginx?

Run openssl pkcs12 -in cert.pfx -passin pass:PWD -clcerts -nokeys -out cert.crt for the leaf, openssl pkcs12 -in cert.pfx -passin pass:PWD -cacerts -nokeys -out chain.crt for intermediates, then cat cert.crt chain.crt > fullchain.crt. Point nginx at fullchain.crt and private.key.

5. Why does OpenSSL say Mac verify error invalid password?

The -passin value does not match the PFX import password. Retry with the export password from Windows IIS, Azure, or whoever created the file. Passwords are case-sensitive.

6. Why do I only see Bag Attributes and no BEGIN PRIVATE KEY?

You omitted -nodes and OpenSSL encrypted the output key, or the PFX never contained a private key. Re-run with -nocerts -nodes, or confirm the PFX was exported with the private key included.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …