--- title: Electronアプリをコード署名してApple 公証 (Notary) を通過させる方法 date: 2019-06-05 00:00:00 +09:00 redirect_from: "/blog/2019/06/05/sign-and-notarize-electron-app" --- electron-builder を利用して macOS 向け Electron アプリをコード署名し、公証を通過させる。 > **tl;dr**: コード署名と公証に対応した macOS アプリ Juno のリポジトリを[GitHub で公開](https://github.com/uetchy/juno)している。 # Code Sign アプリのコード署名は`electron-builder`によって自動で行われる。内部的には[electron-osx-sign](https://github.com/electron-userland/electron-osx-sign)が使用される。 リリース用のアプリにコード署名をするには、Keychain に有効な Developer ID Certificate が格納されている必要がある。macOS Developer Certificate は開発用のコード署名にしか使えないため、リリース用としては不十分だ。 まだ証明書を発行していない場合は、[Apple Developer](https://developer.apple.com/account/resources/certificates/list)で証明書の追加ウィザードに進み、**Developer ID Application**を選択して証明書を発行する。 # Notarize コード署名済みのアプリを[electron-notarize](https://github.com/electron-userland/electron-notarize)を使用して Apple Notary Service に提出する。 ```js const { notarize } = require("electron-notarize"); notarize({ appBundleId, appPath, appleId, appleIdPassword, ascProvider, }); ``` - **appBundleId**: アプリの Bundle ID 。`package.json`の`build.appId`と同じものを使う。 - **appPath**: `.app`の絶対パスを指定する。 - **appleId**: Apple Developer として登録している Apple ID を指定する。 - **appleIdPassword**: Apple ID のパスワード。2 要素認証を必要としないパスワードが必要なので、[Apple ID](https://appleid.apple.com/#!&page=signin)にアクセスして**App-specific Password**を発行する。 - **ascProvider**: Apple Developer の Membership に記載されている**Team ID**を指定する。 ## electron-builder の afterSign フック electron-builder の afterSign フックを使用して、コード署名が済んだアプリを自動で Notary に提出する。 フックスクリプトを`./scripts/after-sign-mac.js`に置く。 ```js const path = require("path"); const { notarize } = require("electron-notarize"); const appleId = process.env.APPLE_ID; const appleIdPassword = process.env.APPLE_PASSWORD; const ascProvider = process.env.ASC_PROVIDER; const configPath = path.resolve(__dirname, "../package.json"); const appPath = path.resolve(__dirname, "../dist/mac/App.app"); const config = require(configPath); const appBundleId = config.build.appId; async function notarizeApp() { console.log(`afterSign: Notarizing ${appBundleId} in ${appPath}`); await notarize({ appBundleId, appPath, appleId, appleIdPassword, ascProvider, }); console.log("afterSign: Notarized"); } exports.default = async () => { await notarizeApp(); }; ``` `package.json`の`build`に`afterSign`を追加して、コード署名が終わった後にスクリプトが実行されるようにする。 ```json "build": { "afterSign": "./scripts/after-sign-mac.js" } ``` ## Hardened Runtime and Entitlements このままでは公証に失敗する。デフォルトで書き出されるバイナリでは、セキュリティの強化された[Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime_entitlements)が有効になっていないためだ。以下のようなエラーメッセージが帰ってくる。 ```json { "status": "Invalid", "statusSummary": "Archive contains critical validation errors", "statusCode": 4000, "issues": [ { "severity": "error", "code": null, "path": "App.zip/App.app/Contents/MacOS/App", "message": "The executable does not have the hardened runtime enabled.", "docUrl": null, "architecture": "x86_64" }, } } ``` そこで、`package.json`の`build.mac.hardenedRuntime`を`true`にして Hardened Runtime を有効にする。 ```json "build": { "mac": { "hardenedRuntime": true } } ``` Hardened Runtime 下では、必要に応じて Entitlement を指定しなければならない。Electron の実行には`allow-unsigned-executable-memory` Entitlement が必要だ。そこで、`entitlement.plist`ファイルを`build`フォルダに作成し、以下のような plist を記述する。 ```xml com.apple.security.cs.allow-unsigned-executable-memory ``` `package.json`の`entitlements`及び`entitlementsInherit`に Entitlement が記述された plist のファイルパスを指定する。 ```json "build": { "mac": { "hardenedRuntime": true, "entitlements": "./src/build/entitlement.plist", "entitlementsInherit": "./src/build/entitlement.plist" } } ``` Hardened Runtime で Electron を実行することができるようになったので、Notary を通過できる状態になった。 実際に`electron-builder`を実行して、すべてのプロセスが滞りなく動作することを確かめよう。 # Verify Notary Status ただしく公証を得られたかどうかは`altool`で調べることができる。 公証通過後に送られてくるメールに`Request Identifier`が記載されているのでメモする。 ``` Dear uetchy, Your Mac software has been notarized. You can now export this software and distribute it directly to users. Bundle Identifier: Request Identifier: For details on exporting a notarized app, visit Xcode Help or the notarization guide. Best Regards, Apple Developer Relations ``` `xcrun altool --notarization-info`コマンドに UUID と Apple ID、パスワードを指定して公証ステータスを確認する。 ``` xcrun altool --notarization-info -u $APPLE_ID -p $APPLE_PASSWORD ``` 正しく公証が得られている場合は以下のようなメッセージが表示される。おめでとう! ``` 2019-06-05 13:51:18.236 altool[5944:261201] No errors getting notarization info. RequestUUID: Date: 2019-06-05 04:45:54 +0000 Status: success LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/ Status Code: 0 Status Message: Package Approved ``` ## 参考文献 - [Resolving Common Notarization Issues](https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/resolving_common_notarization_issues) - [Notarizing your Electron application](https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/) - [Feature request: Enable hardened runtime for macOS #3383](https://github.com/electron-userland/electron-builder/issues/3383)