macOS Catalinaにおけるアプリの「公証(Notarization)」への対応方法

こんにちは
ウェブテクノロジR&Dの佐藤 伸です。

2020年2月以降、macOS 10.15 “Catalina” では、「公証(Notarization)」を受けていない(安全性が確認されていない)アプリは実行できなくなってしまうのですが、ご存知でしたでしょうか。

GateKeeperにより実行を止められたときのダイアログ

これにより、ダウンロードしたアプリの安全性が実行前に確認されるようになるため、ユーザーはより安心してアプリを利用できるようになります。

しかしアプリが公証を受けていないと、ユーザーはそのアプリを突然使えなくなってしまうわけですから大問題です。アプリ開発者には、なるべく早急に公証を受け、アプリをアップデートすることが求められます。

この記事では、macOSアプリで公証を受ける手順についてご紹介します。

公証(Notarization)とは?

「公証」とは、アプリの開発者による署名や開発元の証明について、「改ざんされていない、正規なものである」ことを保証するものです。

参考:https://support.apple.com/ja-jp/HT202491

前もって必要なこと

この記事では、Apple Developer Program の契約、Apple ID の取得、開発者証明書等が取得済みであることを前提としています。

また、Developer ID Application の秘密鍵(p12)が必要なので手元に用意しておいてください。

作業環境

この記事の内容は、以下の環境で確認をしています。

  • macOS Catalina 10.15.2
  • Xcode 11.2.1

公証を受けられる条件

公証を受けられる条件は以下のようになっています。

  • 配布するすべての実行可能ファイルに署名が付いている
  • 強化されたランタイムが有効化されている
  • Developer IDの証明書で署名されている
  • セキュアタイムスタンプが含まれている
  • com.apple.security.get-task-allow 資格情報が含まれていない ※1
  • macOS 10.9 以降のSDKでリンクされている

※1 com.apple.security.get-task-allow の資格はデバッグが容易になる一方、攻撃者が実行時にコードを挿入できるため、これを指定していると公証で失敗するとのことです。

Xcodeでは com.apple.security.get-task-allow の資格情報を外しておく必要があります。

参考:https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/resolving_common_notarization_issues

サンドボックス化の指定

サンドボックス化しなくても公証は受けられますが、AppStoreで配信はできないようです。(※執筆時点では未確認です)

Xcodeを使う場合

まず、アプリに署名を付ける必要があります。
これには、Xcodeを使う場合と使わない場合で手順が異なります。

Xcodeを使うと、自動でアプリに署名を付けるところまでやってくれるので、作業がちょっと楽です。

サンドボックスの設定は、TARGETS、Signing & Capabilities>App Sandbox の設定で行います。

なお、標準で以下の3つが有効になります。

  • com.apple.security.app-sandbox
  • com.apple.security.files.user-selected.read-only
  • com.apple.security.get-task-allow

com.apple.security.get-task-allow が有効になると困るので、これを付かないようにするには TARGETS、Build Seettings>Signing>Code Sining Inject Base Entitlements で「No」を指定します。

 

Xcodeを使わない場合

コマンドラインアプリやQtアプリなどでは、Xcodeを使わないため、手動で資格情報を設定し署名する必要があります。

資格情報ファイルの作成

まず、資格情報ファイル、entitlements.plist を作成しておきます。ファイル名は任意のもので問題ありません。

資格情報ファイルにはcom.apple.security.get-task-allowを含めないようにします。

<?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>com.apple.security.app-sandbox</key>     <!-- サンドボックス化を行う場合 -->
        <true/>
        <key>com.apple.security.network.client</key>  <!-- ネットワークを使う場合 -->
        <true/>
    </dict>
</plist>

サンドボックスのための要素キーはこちらを参照してください。

https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html

通信を行うアプリの場合は、com.apple.security.network.clientを指定しないと通信ができなくなってしまいますので、忘れないようにしてください。

ところで、スペルミスなどで間違ったキーを指定するとアプリが起動しなくなるようです。私はこれに気付かず悩まされましたので、起動しなくなったら疑ってみてください。

アプリに署名を付ける

配布したいバイナリのリリースビルドを用意しておきます。
実行できるバイナリすべてに署名を付けておく必要があります。

ここでは samp.app というアプリが出力されたとして説明します。

Qtアプリの場合は、.app ディレクトリに macdeployqt コマンドを適用しておくのを忘れないようにしましょう。

–sign で指定する署名の識別子は、アプリケーション>ユーティリティから「キーチェーンアクセス > ログイン」で確認できます。

codesign --force --verify --verbose \
    --sign "Developer ID Application: Your Corp." \
    "samp.app" \
    --deep \
    --options runtime \
    --entitlements entitlements.plist \
    --timestamp

オプションの説明は下記のとおりです。

オプション 意味
–deep 使用しているフレームワークなどを再帰的に処理する
–options runtime 指定されたランタイムを有効にする
–entitlements <file> 資格情報ファイルを指定する
–timestamp セキュアタイムスタンプを使用する

署名に成功すると以下のように表示されます。

samp.app: signed app bundle with Mach-O thin (x86_64) [jp.co.your-corp.samp]

codesign コマンドで署名するときに、Mac上のターミナルから行う必要があります。

Windowsなどから sshで行うと失敗します。

私はsshから作業していて、失敗する原因がわからず随分ハマってしまいました。

参考:https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/resolving_common_notarization_issues

署名の確認

ここから先は、Xcodeを使う場合・使わない場合のどちらでも共通な作業です。

まず念の為、下記のコマンドで、ファイルに付けた署名情報を表示し確認してみてください。

codesign --display -vvv samp.app

サンドボックス指定の確認

下記のコマンドで、enetitlements.plist の内容が表示されます。

codesign --display --entitlements :- samp.app

パッケージ化

続いて、アプリをパッケージ化(.dmg, .zip, .pkg,化)します。

これらの作成方法についての説明は省略しますが、.zip化する方法だけ紹介しておきます。

zip化には、zipコマンドではなく下記のように ditto コマンドで、ファイルとは分離されたリソースフォークの情報も一緒にzip化する必要があるので注意してください。

ditto -ck --rsrc --sequesterRsrc "samp.app" "samp.app.zip"

パッケージにも署名を付ける(.dmg, .zip の場合)

ここでは .dmg を生成した場合で説明します。(※2)

下記はパッケージに署名するコマンド例です。–options, –entitlements は不要かもしれません。

codesign --force --verify --verbose \
    --sign "Developer ID Application: Your Corp." \
    "samp.dmg" \
    --deep \
    --options runtime \
    --entitlements entitlements.plist \
    --timestamp

なお、試してみたところ、パッケージに署名が無くても公証を受けることができましたが、ステープラーで留める作業(後述)が失敗するので、パッケージにも署名が必要です。

※2 .dmgの作り方は以下の記事がとてもわかりやすかったです。

カスタマイズした.dmgを作成する方法
https://qiita.com/econa77/items/d0e7d3a75d8fd3bb7777

パッケージに署名を付ける(.pkg の場合)

.pkg の署名は、codesign コマンドではなく、下記のようにproductsign コマンドを使用します。

–sign 引数のあとの Developer ID は、 “Developer ID Application”ではなく、”Developer ID Installer” になります。

こちらも ssh から行うと失敗します。

productsign --sign "Developer ID Installer: Your Corp." \
    samp.pkg samp-signed.pkg

署名の確認は下記コマンドで行えます。

pkgutil --check-signature ./samp-signed.pkg

App用パスワードの作成

App用パスワードは Apple ID のパスワードではありません

App用パスワードをまだ作成してしていない場合は作成しておいてください。

下記のページから作成できます。

https://support.apple.com/ja-jp/HT204397

`****-****-****-****` というような形式のパスワードが発行されます。

パッケージをアップロード

下記のように、xcrun altool コマンドを使ってパッケージをアップロードし、Appleに提出します。

パスワードには前述の「App用パスワード」を指定します。

.pkgの場合は元ファイルでなく、署名を付けたsamp-signed.pkgと間違えないようにしてください。

xcrun altool --notarize-app -t osx -f "samp.dmg" \
    --primary-bundle-id "jp.co.your-corp.samp" \
    -u "<あなたのApple IDのメールアドレス>" \
    -p "****-****-****-****"

アップロードには少し時間がかかります。私の環境では45秒くらいかかりました。

下記のように、リクエストトークンとしてUUIDが返ってきます。

No errors uploading 'samp.dmg'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

ステータスの確認

同じく xcrun altool コマンドを使い、先程のトークンを使ってステータスを確認できます。(※確認しなくても、成功または失敗のメールが届きます)

altool の後に –verbose をつけると詳細が見られます。

xcrun altool \
    --notarization-info xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
    -u "<あなたのApple IDのメールアドレス>" \
    -p "****-****-****-****"

`Status:``success`とあれば成功で、`invalid`だと失敗のようです。

`in progress`とあればまだ処理中なので、もう少し待ちましょう。

CI環境等のスクリプトに組み込んで自動処理するなどの場合は、これらは必要かもしれません。しかしそうでないなら、手作業で行わなくても、待っていれば結果がメールで届くのでそのほうが楽ですね。

ステープラーで留める

最後に、xcrun stapler コマンドを使ってパッケージにチケットを紐付けします。(.dmg, .pkg の場合)

staple の後に -v をつけると詳細が表示されます。

こちらも.pkgの場合は元ファイルでなく、署名を付けたsamp-signed.pkgと間違えないようにしてください。

xcrun stapler staple 'samp.dmg'

ユーザーが macOS 10.14 以降の環境でアプリを実行しようとすると、Gatekeeperはオンラインで、それが公証を受けたアプリなのかを確認するようです。

アプリをステープラーで留めておくことで、ネットワークに接続していないオフライン環境でもGateKeeperが「公証を受けたアプリである」ことを認識してくれるようになっているようです。

成功すると以下が表示されます。

Processing が2行出力されているのは stable と validate をそれぞれ処理しているためのようです。

Processing: /Users/<あなたのユーザー名>/package/samp.dmg
Processing: /Users/<あなたのユーザー名>/package/samp.dmg
The staple and validate action worked!

参考:https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow

参考:https://developer.apple.com/jp/developer-id/

ただし、.zip の場合には直接紐付けはできず、アーカイブ内の個々のバイナリに紐付けすることになるようです。

参考:https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow

公証を受けたアプリの一覧を表示

xcrun altoo lコマンドを使って下記を実行することで、公証を受けたアプリの一覧を表示することができます。

処理中のものもここに表示されます。

xcrun altool --notarization-history \
    -u "<あなたのApple IDのメールアドレス>" \
    -p "****-****-****-****"

お疲れ様でした

これで、2020年2月以降でもCatalinaで実行可能なアプリになりました。早速アップデートをリリースしましょう。

以上が、macOSアプリで公証を受ける手順になります。

この記事が少しでも皆様のお役に立てれば幸いです。

追記

恥ずかしながら私自身、Catalinaの公証が実施されるほんの3ヶ月前に初めて、アプリに公証対応が必須になることを知って、慌てて調査しました。

もっとアンテナを張り巡らせて情報収集しておかないとダメだなと痛感しました。

2020/06/16 更新 |