macOSアプリをdmg(ディスクイメージ)で配布する場合は、dmgを公証してstaple(公証チケットを添付)する。notarytool submitには、zipのほかにdmgも指定できる。

$ codesign --force --timestamp --sign "[Developer ID Applicationの証明書名]" [アプリ名].dmg
$ xcrun notarytool submit [アプリ名].dmg -p "[プロファイル名]" --wait
$ xcrun stapler staple [アプリ名].dmg

ここでは、dmgの作成から、署名、公証、stapleまでの手順を紹介する。実行例はXcode 27.0に含まれるnotarytool 1.1.3のもの。

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

提出できるファイルの形式

submitに指定できるファイルは、zip(.zip)、flat installer package(.pkg)、UDIF形式のディスクイメージ(.dmg)の3種類である。それ以外の形式のファイルは、アップロード前の確認でエラーになる。

$ xcrun notarytool submit hello.tgz -p "[プロファイル名]"
Conducting pre-submission checks for hello.tgz and initiating connection to the Apple notary service...
Error: hello.tgz must be a zip archive (.zip), flat installer package (.pkg), or UDIF disk image (.dmg), use 'notarytool submit --force' to skip this validation and submit anyway.
Usage: notarytool <subcommand>
  See 'notarytool --help' for more information.
$ echo $?
64

.tgzのほか、拡張子が.txtのファイルや、実行ファイルそのものを指定した場合も、同じエラーで終了コード64になる。実行ファイルを公証する場合は、zipに固める。

dmgを作成する

hdiutil createで、フォルダの内容からdmgを作成する。-srcfolderに、dmgに入れるアプリを置いたフォルダを指定する。

$ mkdir dmgsrc
$ cp -R [アプリ名].app dmgsrc/
$ hdiutil create -volname "[ボリューム名]" -srcfolder dmgsrc -ov -format UDZO [アプリ名].dmg

UDZOは、圧縮されたdmgの形式である。macOS 27.0.1では、hdiutil createの実行時に'hdiutil create -volname -format ...' is deprecated. Please use 'diskutil image create from/blank --volumeName --format ...' instead.という警告が表示された。dmgは作成され、公証までの手順に影響はなかった。

dmgに入れるアプリは、あらかじめDeveloper IDで署名し、Hardened Runtimeを有効にしておく。

dmgに署名する

作成したdmgに、codesignでDeveloper ID Applicationの証明書で署名する。--timestampでセキュアタイムスタンプを付ける。

$ codesign --force --timestamp --sign "[Developer ID Applicationの証明書名]" [アプリ名].dmg

codesign -dvで、署名の内容を確認できる。

$ codesign -dv [アプリ名].dmg 2>&1 | grep -E 'Identifier|Format|TeamIdentifier|Timestamp'
Identifier=[アプリ名]
Format=disk image
TeamIdentifier=[Team ID]
Timestamp=[署名した日時]

Format=disk imageと表示される。

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

dmgを公証する

dmgをsubmitに指定する。zipと同じ手順で公証できる。

$ xcrun notarytool submit [アプリ名].dmg -p "[プロファイル名]" --wait
Conducting pre-submission checks for [アプリ名].dmg and initiating connection to the Apple notary service...
Submission ID received
  id: [提出ID]
Successfully uploaded file
  id: [提出ID]
  path: [アプリ名].dmgのパス
Waiting for processing to complete.
Current status: In Progress...Current status: In Progress....Current status: Accepted.....Processing complete
  id: [提出ID]
  status: Accepted

status: Acceptedと表示されれば、公証は通っている。

dmgにstapleする

公証が通ったdmgに、stapler stapleで公証チケットを添付する。

$ xcrun stapler staple [アプリ名].dmg
Processing: [アプリ名].dmgのパス
Processing: [アプリ名].dmgのパス
The staple and validate action worked!

stapler validateで、チケットが添付されていることを確認できる。

$ xcrun stapler validate [アプリ名].dmg
Processing: [アプリ名].dmgのパス
The validate action worked!

spctlで、Gatekeeperの判定を確認できる。--type open --context context:primary-signatureを指定して、dmgを評価する。

$ spctl --assess --type open --context context:primary-signature -vv [アプリ名].dmg
[アプリ名].dmg: accepted
source=Notarized Developer ID
origin=Developer ID Application: [名前] ([Team ID])

source=Notarized Developer IDと表示されれば、公証済みのdmgとして判定されている。

参考: 【spctl】Gatekeeperの判定を配布前に確認する

dmgに署名しないとGatekeeperに拒否される

署名していないdmgでも、公証はAcceptedになった。このとき、dmgに入れた実行ファイルは署名済みだった。stapleも成功する。

$ xcrun notarytool submit [署名していないアプリ名].dmg -p "[プロファイル名]" --wait
...
  status: Accepted
$ xcrun stapler staple [署名していないアプリ名].dmg
...
The staple and validate action worked!

ところが、spctlの判定はrejectedになる。

$ spctl --assess --type open --context context:primary-signature -vv [署名していないアプリ名].dmg
[署名していないアプリ名].dmg: rejected
source=no usable signature
$ echo $?
3

公証がAcceptedになっても、dmg自体に署名がなければ、配布物として使えない。公証の前に、dmgに署名する。

zipはstapleできない

staplerは、zipにチケットを添付できない。

$ xcrun stapler staple [アプリ名].zip
Processing: [アプリ名].zipのパス
Stapler is incapable of working with ZIP archive files.
$ echo $?
66

zipで公証した場合は、zipの中のアプリにstapleしてから、zipを作り直す。dmgであれば、dmg自体にstapleできる。

形式公証staple
zipできるできない
dmgできるできる

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