Visual Studio App CenterでmacOSアプリのNotarizationに対応しようとした

f:id:iseebi:20190724001404p:plain

macOS Catalinaから、macOSアプリはAppleによる公証(Notarization)が必須化されます。Notarizationのプロセスではアプリのバイナリを一度Appleに送って、マルウェアなどのチェックを受けます。雰囲気は以下のページがわかりやすいです。

applech2.com

さて、自分もTransporterPadというmacOSアプリを作っています。CIには無料でmacOSアプリのビルドができるVisual Studio App Centerを使っていますが、公式な対応はしばらくかかりそうな様子でした。

github.com

そこで、カスタムビルドスクリプトを使って対応させることにしました。

事前に必要なこと

Notarizeするには、アプリが以下の条件を満たしている必要があります。

  • アプリ内のすべての実行ファイルが署名されていること。
  • Developer ID の証明書で署名されていること
  • セキュアタイムスタンプが署名に含まれていること
  • Hardened Runtimeを使用する設定になっていること
  • macOS 10.9以上のSDKでビルドされていること
  • get-task-allowのEntitlementが含まれていないこと

特にHardened Runtimeについてですが、これはXcodeでここをONにするだけで対応が可能です。

f:id:iseebi:20190723235944p:plain

App Centerのカスタムビルドスクリプト

App CenterではPost-clone、Pre-build、Post-buildの3種類のカスタムビルドスクリプトを使うことができます。

docs.microsoft.com

今回はビルド後に動作したいので、appcenter-post-build.shを実行権限をつけた上でリポジトリに含めました。

このスクリプトに export とか find とかを書いて何度か試したところ、成果物としては$APPCENTER_OUTPUT_DIRECTORY の場所に (プロジェクト名)_distribution.zip(プロジェクト名)_download.zip ができており、恐らく前者が AppStore 送信用、後者がApp Centerのコンソールからダウンロードしたときに降ってくるファイルです。(プロジェクト名)_download.zipの方には関連するplistなども入っているため、Notarizeに投げるものとしては$APPCENTER_OUTPUT_DIRECTORY/(プロジェクト名)_distribution.zipが適切でしょう。

NotarizationをPost-buildで実行する

では、appcenter-post-build.shにNotarizationを実行するスクリプトを記述します。コマンドなどは以下を参考に。

developer.apple.com

BUNDLE_IDENTIFIER=net.iseteki.TransporterPad
DISTRIBUTION_FILE=$APPCENTER_OUTPUT_DIRECTORY/TransporterPad_distribution.zip
xcrun altool --notarize-app --primary-bundle-id $BUNDLE_IDENTIFIER --username $AC_USERNAME --password $AC_PASSWORD --file $DISTRIBUTION_FILE

$AC_USERNAME$AC_PASSWORDApp Store ConnectにサインインするApple IDの認証情報です。アカウントで2ファクタ認証が有効になっている場合、パスワードはApp用パスワードをMy Apple IDで発行して使います。

Apple IDの情報はApp Centerのビルド設定の環境変数から与えます。

f:id:iseebi:20190723234209p:plain

こんな感じになれば成功。

f:id:iseebi:20190723234728p:plain

Error: To use this application, you must first sign in to iTunes Connect and sign the relevant contracts.というエラーでうまくいかない場合は、App Store Connectの契約情報のところを確認してみてください。すべてグリーンになっていないと失敗するみたいです。

結果を確認する

ローカルマシンでxcrun altool --notarization-history 0を実行すると、結果を確認できます。

$ xcrun altool --notarization-history 0 -u $AC_USERNAME -p $AC_PASSWORD

Notarization History - page 0

Date                      RequestUUID                          Status  Status Code Status Message  
------------------------- ------------------------------------ ------- ----------- --------------- 
2019-07-23 14:11:09 +0000 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx invalid 2           Package Invalid 

Next page value: 1563891069000

ちょ…invalid?????

詳細を確認してみましょう。xcrun altool --notarization-info $RequestUUIDで、詳細を確認できます。

$ xcrun altool --notarization-info fd2790db-dbc4-4e1c-a38c-bca4ffff9284 -u $AC_USERNAME -p $AC_PASSWORD
2019-07-23 23:53:01.397 altool[56209:21692262] No errors getting notarization info.

   RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          Date: 2019-07-23 14:11:09 +0000
        Status: invalid
    LogFileURL: https://osxapps-ssl.itunes.apple.com/...
   Status Code: 2
Status Message: Package Invalid

LogFileURLの内容を見てみましょう。

$ curl "https://osxapps-ssl.itunes.apple.com/..." | jq '.'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1016  100  1016    0     0    951      0  0:00:01  0:00:01 --:--:--   975
{
  "logFormatVersion": 1,
  "jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "Invalid",
  "statusSummary": "Archive contains critical validation errors",
  "statusCode": 4000,
  "archiveFilename": "TransporterPad_distribution.zip",
  "uploadDate": "2019-07-23T14:11:09Z",
  "sha256": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "ticketContents": null,
  "issues": [
    {
      "severity": "error",
      "code": null,
      "path": "TransporterPad_distribution.zip/TransporterPad.app/Contents/Resources/android-platform-tools/adb",
      "message": "The executable does not have the hardened runtime enabled.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "TransporterPad_distribution.zip/TransporterPad.app/Contents/Resources/ios-deploy/ios-deploy",
      "message": "The executable does not have the hardened runtime enabled.",
      "docUrl": null,
      "architecture": "x86_64"
    }
  ]
}

なんということでしょう。アプリ本体はHardened Runtimeを使用する設定になっているものの、TranspoterPadはサブコマンドとして埋め込まれているadbとios-deployがHardened Runtimeに対応していません!このアプリをNotarizeするには、まずadbとios-deployをHardened Runtimeにしてビルドしなくてはならないということです。

このあとは

もし、これがうまくいった場合、.appにAppleの公証済み情報を埋め込むstapleという処理を施す追加処理をすることでオフライン環境下でもアプリを起動させることができます。(stapleをしない場合、実行時にインターネット経由で取得することになる)

今のApp Centerでこれをするには、一度zipを展開してstaple後、再圧縮となると思われます。上記の問題が解決できたら、また書こうと思います。