秘密鍵と証明書を1つのファイルにまとめたPFX(.pfx、.p12)を作りたいときは、openssl pkcs12 -exportを使う。Windowsの証明書ストアやmacOSのキーチェーン、Javaのキーストアなどに、秘密鍵付きの証明書を取り込む場合に使う。

$ openssl pkcs12 -export -inkey server.key -in server.crt -out server.p12
Enter Export Password:
Verifying - Enter Export Password:

-inkeyが秘密鍵、-inが証明書、-outが出力するPFXファイルである。Export Passwordは、PFXファイルを保護するパスワードである。

PFXファイルからPEM形式の秘密鍵や証明書を取り出すには、-exportなしで実行する。

$ openssl pkcs12 -in server.p12 -out all.pem -noenc
Enter Import Password:

PFXとPKCS#12は同じものを指す。.pfxと.p12の拡張子の違いは、中身に影響しない。

実行例はUbuntu 24.04のOpenSSL 3.0.13のもの。

パスワードを非対話で指定する

スクリプトから実行する場合は、-passout(作成時)と-passin(展開時)でパスワードを渡す。指定方法はpass:、env:、file:で、openssl encの-passと共通である。

$ export P12PW=secret
$ openssl pkcs12 -export -inkey server.key -in server.crt -out server.p12 -passout env:P12PW
$ openssl pkcs12 -in server.p12 -nokeys -passin env:P12PW

pass:はコマンドラインに残るため、共有環境ではenv:かfile:を使う。

参考: 【openssl】ファイルをパスワードで暗号化・復号する (enc)

中間CAの証明書や名前を含める

-certfileで、CAの証明書を一緒に格納できる。-nameは、キーチェーンやブラウザーの一覧に表示される名前(フレンドリーネーム)を付ける。

$ openssl pkcs12 -export -inkey server.key -in server.crt -certfile ca.crt \
    -name "server" -out server.p12

中間CAを含めると、取り込み先がチェーンを構成できる。

PEM形式の証明書と秘密鍵を連結した1つのファイルを、-inだけで渡すこともできる。

$ cat server.crt server.key > combined.pem
$ openssl pkcs12 -export -in combined.pem -out combined.p12

秘密鍵と証明書のペアが違うと、作成時にエラーで失敗する。

$ openssl pkcs12 -export -inkey other.key -in server.crt -out bad.p12
No cert in -in file 'server.crt' matches private key

参考: 【openssl】秘密鍵と証明書が対応しているか確認する (pkey -pubout)

PFXの中身を確認する

-info -nooutで、中身の構成を確認できる。

$ openssl pkcs12 -in server.p12 -info -noout
Enter Import Password:
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

Certificate bagが証明書、Shrouded Keybagが暗号化された秘密鍵である。MACとPBES2の行から、使われている暗号化方式を確認できる。

パスワードが違う場合は、Mac verify error: invalid password?というエラーになる。

PFXからPEMを取り出す

取り出す対象は、オプションで選ぶ。

オプション取り出す内容
なし証明書と秘密鍵のすべて
-nokeys証明書だけ
-nocerts秘密鍵だけ
-clcertsサーバー証明書だけ(CA証明書を除く)
-cacertsCA証明書だけ
$ openssl pkcs12 -in server.p12 -nokeys -passin env:P12PW
Bag Attributes
    friendlyName: server
subject=CN = server.test
issuer=CN = Test CA
-----BEGIN CERTIFICATE-----
...

秘密鍵をパスフレーズなしで取り出す

-nocertsや、オプションなしで秘密鍵を取り出すと、PEMのパスフレーズの入力を求められる。パスフレーズなしで取り出すには、-noencを付ける。

$ openssl pkcs12 -in server.p12 -nocerts -noenc -passin env:P12PW -out server.key

パスフレーズなしの秘密鍵は、ファイルが漏れるとそのまま悪用される。パーミッションを600にして保管する。

-noencは、OpenSSL 3.0で導入されたオプションである。古い記事で見かける-nodesは、同じ意味の非推奨の名前である。

取り出した証明書にヘッダーが付く

PFXから取り出したPEMには、Bag AttributesやfriendlyNameの行が付く。openssl x509などは、PEM以外の行を読み飛ばすため、そのまま使える。気になる場合は、openssl x509 -in all.pemで整形し直す。

macOSのキーチェーンに読み込めない

OpenSSL 3.0以降のpkcs12 -exportは、デフォルトで新しい暗号化方式(AES-256-CBCとPBKDF2、MACにSHA-256)を使う。この形式のPFXは、macOSのsecurity importで読み込めない。

$ security import server.p12 -k test.keychain-db -P secret
security: SecKeychainItemImport: MAC verification failed during PKCS12 import (wrong password?)

パスワードが正しくても、wrong password?というエラーになる。実際の原因はパスワードではなく、暗号化方式である。この例はmacOS 26.6.2で確認した。

-legacyを付けて作り直す

-legacyを付けて作り直すと、古い暗号化方式(秘密鍵は3DES、証明書は40ビットRC2、MACはSHA-1)で作られ、読み込める。

$ openssl pkcs12 -export -legacy -inkey server.key -in server.crt -out legacy.p12
$ openssl pkcs12 -in legacy.p12 -info -noout -legacy
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 16
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
$ security import legacy.p12 -k test.keychain-db -P secret
1 identity imported.

-legacyは、OpenSSL 3.0で導入された、レガシープロバイダーを有効にするオプションである。OpenSSL 1.1.1以前やLibreSSLでは使えない。

古い暗号化方式は、強度が低い。取り込み先が新しい方式に対応している場合は、デフォルトの形式を使う。

macOS標準のopensslで作る

macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)は、デフォルトで古い暗号化方式のPFXを作る。そのため、-legacyなしでsecurity importできる。

$ /usr/bin/openssl pkcs12 -export -inkey server.key -in server.crt -out libre.p12
$ security import libre.p12 -k test.keychain-db -P secret
1 identity imported.

LibreSSL 3.3.6は、OpenSSL 3.xが作ったPFXの読み込みもできる。

$ /usr/bin/openssl pkcs12 -in modern.p12 -info -noout
MAC Iteration 2048
MAC verified OK

古い暗号化方式を展開する

OpenSSL 3.xで、古い暗号化方式のPFXを展開する場合も、-legacyが必要になる。-legacyを付けないと、RC2が使えず、展開に失敗する。

$ openssl pkcs12 -in legacy.p12 -nokeys
Error outputting keys and certificates
...Algorithm (RC2-40-CBC : 0)...
$ openssl pkcs12 -in legacy.p12 -nokeys -legacy

この例はmacOSのHomebrew版OpenSSL 3.6で確認した。

取り込み先の暗号化方式を指定する

-legacyの一括指定ではなく、暗号化方式を個別に指定する方法もある。

$ openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 \
    -inkey server.key -in server.crt -out old.p12
オプション指定する内容
-keypbe秘密鍵の暗号化方式
-certpbe証明書の暗号化方式
-macalgMACのハッシュアルゴリズム

要求される方式が分かっている場合に使う。分からない場合は、まず-legacyを試す。

バージョンによる違い

macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)では、以下が異なる。

  • -noencに対応していない。代わりに-nodesを使う
  • -legacyに対応していない。デフォルトで古い暗号化方式のPFXを作る

参考

openssl-pkcs12 - OpenSSL Documentation