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:
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:
openssl rsa -in private.key -noout -checkRSA key okThe 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:
openssl pkcs12 -in cert.pfx -passin pass:YOUR_PFX_PASSWORD -info -nooutMAC: 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 hmacWithSHA256Two 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)
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-nocerts -nodes -out server.key
chmod 600 server.keyOpenSSL 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:
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-nocerts -out server-encrypted.keyOr set the output password on the command line:
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-nocerts -passout pass:KeyPass456 -out server-encrypted.keyDecrypt later for nginx:
openssl rsa -in server-encrypted.key -passin pass:KeyPass456 -out server.keyPass PFX password without shell history
read -s PFXPASS
openssl pkcs12 -in server.pfx -passin env:PFXPASS -nocerts -nodes -out server.key
unset PFXPASSFrom a file (restrict permissions):
chmod 600 pfx.pass
openssl pkcs12 -in server.pfx -passin file:pfx.pass -nocerts -nodes -out server.key
shred -u pfx.passExtract 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):
openssl pkcs12 -in nopass.pfx -passin pass: -nocerts -nodes -out private.keyIf OpenSSL still fails, the file is not passwordless—try the export password from the team that generated the PFX.
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):
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-clcerts -nokeys -out server.crtsubject=CN=nginx.example.testCA / intermediate certificates (-cacerts):
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-cacerts -nokeys -out ca-chain.crtFull chain for nginx (leaf first, then intermediates):
cat server.crt ca-chain.crt > fullchain.crtnginx example:
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):
openssl pkcs12 -in server.pfx -passin pass:ExportPass123 \
-nodes -out bundle.pemThe 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:
openssl pkcs12 -in legacy.pfx -passin pass:Legacy123 -legacy \
-nocerts -nodes -out private.keyThe same flag works when creating PFX files for older Windows tools:
openssl pkcs12 -export -in cert.crt -inkey private.key -out legacy.pfx \
-passout pass:Legacy123 -legacyVerify key matches certificate
After extraction, confirm the private key belongs to the certificate before you reload a web server:
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5Matching MD5(stdin)= lines mean the pair is correct. For full cert details, see View certificate with OpenSSL.
Test TLS once nginx is configured:
openssl s_client -connect your.host.name:443 -servername your.host.name </dev/null 2>/dev/null | \
openssl x509 -noout -subject -datesEnd-to-end script for nginx
Adjust paths and password handling for production (avoid pass: in scripts when possible):
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.keyMove 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:
Mac verify error: invalid password?Security notes
- The PFX password protects the bundle at rest;
-nodesoutput 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
- openssl-pkcs12 manual
- Generate self-signed certificate (create a PFX)
- View certificate with OpenSSL
- Install SSL certificate on Nginx
- OpenSSL config and paths
- OpenSSL tutorial hub
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.

