p12 ファイルは秘密鍵だけじゃない

どうも亀山です。急に雪が降ってきました。気温も低いので、風邪などにお気をつけください。

今回は、Mac アプリを CI でビルドするときに証明書周りで発生したエラーを調べる中で知った p12 ファイルの仕組みについて説明します。

p12 ファイルはキーチェーンアクセスから書き出すことができる、秘密鍵を受け渡すためのファイル形式です。iOS 開発者にはおなじみです。

CI で Mac アプリをビルドして署名を行うために、security import コマンドを使って秘密鍵をインポートする下記のスクリプトを書きました。Base64化したバイナリからファイルを生成してインポートするというコードを繰り返しています。それぞれ証明書2つと秘密鍵2つ分あります。

set -ex

security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -t 3600 -u $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

echo "$DEVELOPER_ID_APPLICATION_KEY_BASE64" | base64 --decode > developer_id_application.p12
security import developer_id_application.p12 -k $KEYCHAIN_PATH -P $CERTIFICATE_PASSWORD -T /usr/bin/codesign -T /usr/bin/pkgbuild
rm developer_id_application.p12

echo "$DEVELOPER_ID_INSTALLER_KEY_BASE64" | base64 --decode > developer_id_installer.p12
security import developer_id_installer.p12 -k $KEYCHAIN_PATH -P $CERTIFICATE_PASSWORD -T /usr/bin/codesign -T /usr/bin/pkgbuild
rm developer_id_installer.p12

echo "$DEVELOPER_ID_APPLICATION_CER_BASE64" | base64 --decode > developer_id_application.cer
security import developer_id_application.cer -k $KEYCHAIN_PATH -T /usr/bin/codesign -T /usr/bin/pkgbuild
rm developer_id_application.cer

echo "$DEVELOPER_ID_INSTALLER_CER_BASE64" | base64 --decode > developer_id_installer.cer
security import developer_id_installer.cer -k $KEYCHAIN_PATH -T /usr/bin/codesign -T /usr/bin/pkgbuild
rm developer_id_installer.cer

security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

環境構築のために用意した p12 ファイルでは上記のエラーは出なかったのですが、本番用の p12 を使ったとき以下のようなエラーがでました。

security: SecKeychainItemImport: The specified item already exists in the keychain.

どうやら cer ファイル (証明書) をインポートする際に、すでに証明書が存在するというエラーのようです。これは p12 に同じ証明書が含まれていたために発生しました。

対応方法

証明書を含まない p12 ファイルを書き出すか、cer のインポートを行わないようにします。通常、証明書を含んだ p12 ファイルを使っていると思うので cer のインポートは必要ないでしょう。

証明書の確認

p12 に証明書が含まれているかどうか下記の openssl コマンドで知ることができました。

openssl pkcs12 -info -in 証明書.p12 -nokeys -legacy

証明書が入っている場合

キーチェーンアクセスで見ると > マークが付いていて証明書と紐づけされています。これを右クリックで ...を書き出す を選択して p12 ファイルを生成すると、証明書が同梱された p12 になります。

前述の openssl コマンドで確認すると証明書の情報が取得できます。

MAC: sha1, Iteration 1
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
Bag Attributes
    friendlyName: Developer ID Installer: covelline, LLC. (K45ES832VB)
    localKeyID: 
subject=UID = K45ES832VB, CN = "Developer ID Installer: covelline, LLC. (K45ES832VB)", OU = K45ES832VB, O = "covelline, LLC.", C = US
issuer=CN = Developer ID Certification Authority, OU = Apple Certification Authority, O = Apple Inc., C = US
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048

証明書が入っていない場合

キーチェーンアクセスで見ると > マークが付いておらず、証明書と紐づけされていない事がわかります。キーチェーンアクセス上で証明書を削除すると秘密鍵だけが残り、この状態になります。書き出すと証明書が同梱されていない p12 になります。

先程と同様に openssl コマンドで証明書の情報を取得しようとすると、何も出てきません。

MAC: sha1, Iteration 1
MAC length: 20, salt length: 8
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048

まとめ

security import コマンドで p12 ファイルをインポートすると、同梱された証明書が一緒にインポートされます。cer ファイルをインポートする際に重複するとエラーになります。キーチェーンアクセスで秘密鍵を書き出す際に、証明書が紐づけられていると証明書が同梱された p12 ファイルになります。openssl コマンドで p12 に証明書が同梱されているか確認できます。