アプリの署名が正しく付いているか、公証の前に確認したいときは、codesignの2つのオプションを使う。署名の内容は-dvで表示し、署名の整合性は--verifyで検証する。

$ codesign -dv --verbose=4 [アプリ名].app
$ codesign --verify --deep --strict --verbose=2 [アプリ名].app

実行例はXcode 27.0、macOS 27.0.1のもの。

-dvで署名の内容を表示する

codesign -dは署名の情報を表示し、-vを重ねると詳細になる。-dv --verbose=4で、署名に関するほぼすべての情報が表示される。出力は標準エラー出力に書かれるため、grepなどで絞り込むときは2>&1を付ける。

$ codesign -dv --verbose=4 [アプリ名].app
Executable=[アプリ名].app/Contents/MacOS/[アプリ名]
Identifier=[Bundle ID]
Format=app bundle with Mach-O thin (arm64)
CodeDirectory v=20500 size=488 flags=0x10000(runtime) hashes=4+7 location=embedded
...
CDHash=[CDHash]
Signature size=9044
Authority=Developer ID Application: [名前] ([Team ID])
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Oct 4, 2026 at 7:30:10
Info.plist entries=19
TeamIdentifier=[Team ID]
Runtime Version=27.0.0
Sealed Resources version=2 rules=13 files=0
Internal requirements count=1 size=220
Total signatures=1
Chosen signature=1

公証の前に確認したい行は、次の4つである。

行見方
flags=0x10000(runtime)Hardened Runtimeが有効である。公証に必要
Authority=Developer ID Application: ...Developer ID Applicationで署名されている。Authorityの行は、署名に使った証明書から、中間CA、ルートCAへと連なる
Timestamp=...セキュアタイムスタンプが付いている。公証に必要
TeamIdentifier=...署名したTeam

IdentifierはBundle IDである。CDHashは、署名の対象になったコードのハッシュ値である。

公証してstapler stapleでチケットを添付したアプリには、Notarization Ticket=stapledの行も表示される。

セキュアタイムスタンプの有無

セキュアタイムスタンプは、署名時にAppleのタイムスタンプサーバーが付ける時刻の証明である。付いていると、Timestamp=の行が表示される。Xcodeで開発用の証明書(Apple Development)で署名したアプリには付かず、Signed Time=の行だけが表示される。

$ codesign -dv --verbose=4 [Apple Developmentで署名したアプリ].app 2>&1 | grep -E 'flags|Authority|Time'
CodeDirectory v=20400 size=768 flags=0x0(none) hashes=13+7 location=embedded
Authority=Apple Development: [名前] ([ID])
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Oct 4, 2026 at 7:29:19

Timestamp=がないアプリは、公証でInvalidになる。codesignで署名するときは、--timestampを付ける。

署名に使われた証明書を特定する

Authorityには、証明書の名前だけが表示される。同じ名前の証明書が複数ある場合は、署名に使われた証明書を--extract-certificatesで取り出して、SHA-1ハッシュを確認する。

$ mkdir certs && cd certs
$ codesign -d --extract-certificates=cert [アプリ名].app
$ /usr/bin/openssl x509 -inform DER -in cert0 -noout -fingerprint -sha1
SHA1 Fingerprint=[コロン区切りのSHA-1ハッシュ]

cert0が署名に使った証明書で、cert1以降が中間CAとルートCAである。

Designated Requirementを表示する

-d -r-で、署名のDesignated Requirement(このアプリだと判定するための条件)を表示できる。

$ codesign -d -r- [アプリ名].app
designated => anchor apple generic and identifier "[Bundle ID]" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = [Team ID])

Appleが発行した証明書で、Bundle IDとTeam IDが一致するアプリである、という条件が書かれている。

entitlementsを表示する

署名に埋め込まれたentitlementsは、--entitlements -で表示できる。

$ codesign -d --entitlements - [アプリ名].app
[Dict]
	[Key] com.apple.security.files.user-selected.read-only
	[Value]
		[Bool] true

公証では、com.apple.security.get-task-allowがtrueのアプリがInvalidになる。entitlementsは、署名後のアプリで確認する。

--verifyで署名の整合性を検証する

codesign --verifyは、署名とアプリの中身が一致しているかを検証する。問題がなければ、何も表示されないか、--verbose=2でメッセージが表示される。

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

オプションの意味は次のとおりである。

オプション意味
--verify署名を検証する
--deepアプリに入っているフレームワークやXPCサービスなども検証する
--strict検証を厳密にする
--verbose=2検証の結果を表示する

valid on diskは署名とファイルの内容が一致していること、satisfies its Designated Requirementは署名がDesignated Requirementを満たしていることを表す。

フレームワークやXPCサービスも検証される

Sparkleのように、フレームワークやXPCサービスを同梱したアプリでは、--deepによって内部のコードも検証される。--verbose=2では、検証の準備と結果が--prepared:と--validated:で表示される。

$ codesign --verify --deep --strict --verbose=2 [アプリ名].app
--prepared:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/XPCServices/Downloader.xpc
--validated:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/XPCServices/Downloader.xpc
--prepared:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/XPCServices/Installer.xpc
--validated:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/XPCServices/Installer.xpc
--prepared:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/Updater.app
--validated:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/Updater.app
--prepared:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/Autoupdate
--validated:[アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/Autoupdate
[アプリ名].app: valid on disk
[アプリ名].app: satisfies its Designated Requirement

内部のコードの署名が欠けていると、公証でInvalidになる。内部のコードも、-dvでDeveloper IDの署名とタイムスタンプ、runtimeのフラグが付いているかを確認できる。Xcodeでエクスポートしたアプリは、同梱したSparkleのコードにも、同じ署名が付く。

$ codesign -dv --verbose=2 [アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate 2>&1 | grep -E 'flags|Authority=Dev|Timestamp'
CodeDirectory v=20500 size=1023 flags=0x10000(runtime) hashes=20+7 location=embedded
Authority=Developer ID Application: [名前] ([Team ID])
Timestamp=Oct 4, 2026 at 8:01:36

署名後にファイルを変更すると検証に失敗する

署名の後でファイルを追加したり、書き換えたりすると、検証に失敗する。

$ cp -R [アプリ名].app tamper.app
$ echo x >> tamper.app/Contents/Resources/extra.txt
$ codesign --verify --deep --strict --verbose=2 tamper.app
tamper.app: a sealed resource is missing or invalid
file added: tamper.app/Contents/Resources/extra.txt
$ echo $?
1

a sealed resource is missing or invalidは、署名で封印したリソースが変更されたことを表す。追加されたファイルは、file added:で表示される。終了コードは1である。

署名後にアプリの中身を変更する処理(Info.plistの書き換えなど)は、署名の前に済ませる。

内部のコードに署名がないアプリの検証

内部のフレームワークや実行ファイルの署名が欠けていると、--deepの検証が失敗する。

$ codesign --remove-signature [アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate
$ codesign --verify --deep --strict --verbose=2 [アプリ名].app
[アプリ名].app: code object is not signed at all
In subcomponent: [アプリ名].app/Contents/Frameworks/Sparkle.framework/Versions/Current/Autoupdate
In architecture: arm64

署名のないコードを含むアプリを公証に提出すると、結果がInvalidになる。公証のログには、署名のないバイナリのパスと、The binary is not signed.が記録される。

署名を無効にしたビルドの検証

署名を無効にしてビルドしたアプリは、完全には無署名にならない。CODE_SIGNING_ALLOWED=NOでビルドすると、リンカーが実行ファイルにad hoc署名を付ける。

$ xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release \
    CODE_SIGNING_ALLOWED=NO build
$ codesign -dv --verbose=2 [アプリ名].app
Identifier=[アプリ名]
Format=app bundle with Mach-O thin (arm64)
CodeDirectory v=20400 size=1671 flags=0x20002(adhoc,linker-signed) hashes=49+0 location=embedded
Signature=adhoc
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
Internal requirements=none
$ codesign --verify --deep --strict --verbose=2 [アプリ名].app
[アプリ名].app: code has no resources but signature indicates they must be present

IdentifierがBundle IDではなく実行ファイルの名前になり、TeamIdentifierも設定されない。署名はアプリ全体ではなく、実行ファイルだけに付いている。配布用の署名とは別物である。

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

参考: 【notarytool】公証に失敗した理由をログから調べる