Mac App Store外で配布するmacOSアプリは、アーカイブを作ってからDeveloper ID署名のアプリとしてエクスポートする。xcodebuildでは、archiveと-exportArchiveの2段階で実行する。

$ xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release \
    -archivePath build/[アプリ名].xcarchive archive
$ xcodebuild -exportArchive -archivePath build/[アプリ名].xcarchive \
    -exportPath build -exportOptionsPlist ExportOptions.plist

実行すると、build/[アプリ名].appにDeveloper ID Applicationで署名されたアプリが出力される。実行例はXcode 27.0、macOS 27.0.1のもの。事前に、Developer ID Application証明書がキーチェーンに登録されている必要がある。

ExportOptions.plistを用意する

エクスポートの設定は、ExportOptions.plistに書く。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>developer-id</string>
    <key>teamID</key>
    <string>[Team ID]</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>signingCertificate</key>
    <string>Developer ID Application</string>
</dict>
</plist>

各キーの意味は次のとおりである。

キー値
method配布方法。developer-idは、Mac App Store外でのDeveloper ID配布
teamIDApple DeveloperのTeam ID
signingStyle署名方法。manualは、署名に使う証明書を自分で指定する
signingCertificate署名に使う証明書。名前またはSHA-1ハッシュを指定する

Team IDは、Apple Developerの「Membership details」で確認できる。

アーカイブを作る

archiveで、ビルドの成果物を.xcarchiveにまとめる。

$ xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release \
    -archivePath build/[アプリ名].xcarchive archive
...
** ARCHIVE SUCCEEDED **

署名の設定がAutomaticのままであれば、アーカイブ内のアプリはApple Developmentの証明書で署名される。

$ codesign -dv --verbose=2 build/[アプリ名].xcarchive/Products/Applications/[アプリ名].app 2>&1 | grep Authority
Authority=Apple Development: [名前] ([ID])
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA

配布に使うのは、次のエクスポートで再署名したアプリである。

エクスポートする

-exportArchiveで、アーカイブからDeveloper ID署名のアプリを取り出す。

$ xcodebuild -exportArchive -archivePath build/[アプリ名].xcarchive \
    -exportPath build -exportOptionsPlist ExportOptions.plist
Exported [アプリ名] to: [出力先のパス]
** EXPORT SUCCEEDED **

-exportPathに指定したディレクトリには、次のファイルが出力される。

$ ls build
DistributionSummary.plist
ExportOptions.plist
Packaging.log
[アプリ名].app
[アプリ名].xcarchive

[アプリ名].appが、Developer ID署名のアプリである。Packaging.logには、エクスポートの処理のログが記録される。

署名を確認する

エクスポートしたアプリは、Developer ID Applicationで署名されている。

$ codesign -dv --verbose=2 build/[アプリ名].app 2>&1 | grep -E 'flags|Authority|Timestamp|TeamIdentifier'
CodeDirectory v=20500 size=488 flags=0x10000(runtime) hashes=4+7 location=embedded
Authority=Developer ID Application: [名前] ([Team ID])
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=[署名した日時]
TeamIdentifier=[Team ID]

AuthorityにDeveloper ID Applicationが表示され、Timestampが含まれている。flags=0x10000(runtime)は、Hardened Runtimeが有効であることを表す。

署名の整合性は、codesign --verifyで検証できる。

$ codesign --verify --deep --strict --verbose=2 build/[アプリ名].app
build/[アプリ名].app: valid on disk
build/[アプリ名].app: satisfies its Designated Requirement

どの証明書で署名されたかを確認する

同じ名前のDeveloper ID Application証明書が複数あると、codesignのAuthorityでは見分けられない。エクスポート先に出力されるDistributionSummary.plistには、署名に使った証明書の情報が記録される。

$ plutil -extract '[アプリ名].0.certificate' json -o - build/DistributionSummary.plist
{"SHA1":"[SHA-1ハッシュ]","type":"Developer ID Application","dateExpires":"2031\/09\/17"}

SHA-1ハッシュが、security find-identity -v -p codesigningで表示される証明書のハッシュと一致していれば、意図した証明書で署名されている。

同名の証明書が複数ある環境では、ExportOptions.plistのsigningCertificateにSHA-1ハッシュを指定すると、使う証明書を確実に固定できる。

<key>signingCertificate</key>
<string>[SHA-1ハッシュ]</string>

DistributionSummary.plistには、アプリのバージョン、ビルド番号、署名時のentitlementsなども記録される。

参考: 【codesign】署名の内容と整合性を検証する

参考: 【notarytool】アプリを公証してstaple(公証チケットを添付)する

参考: 【GitHub Actions】CIでだけ署名設定をDeveloper IDに切り替える