electron-builder を利用して macOS 向け Electron アプリをコード署名し、公証を通過させる。
tl;dr: コード署名と公証に対応した macOS アプリ Juno のリポジトリをGitHub で公開している。
Code Sign
アプリのコード署名はelectron-builder
によって自動で行われる。内部的にはelectron-osx-signが使用される。
リリース用のアプリにコード署名をするには、Keychain に有効な Developer ID Certificate が格納されている必要がある。macOS Developer Certificate は開発用のコード署名にしか使えないため、リリース用としては不十分だ。
まだ証明書を発行していない場合は、Apple Developerで証明書の追加ウィザードに進み、Developer ID Applicationを選択して証明書を発行する。
Notarize
コード署名済みのアプリをelectron-notarizeを使用して Apple Notary Service に提出する。
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にアクセスしてApp-specific Passwordを発行する。
- ascProvider: Apple Developer の Membership に記載されているTeam IDを指定する。
electron-builder の afterSign フック
electron-builder の afterSign フックを使用して、コード署名が済んだアプリを自動で Notary に提出する。
フックスクリプトを./scripts/after-sign-mac.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
を追加して、コード署名が終わった後にスクリプトが実行されるようにする。
"build": {
"afterSign": "./scripts/after-sign-mac.js"
}
Hardened Runtime and Entitlements
このままでは公証に失敗する。デフォルトで書き出されるバイナリでは、セキュリティの強化されたHardened Runtimeが有効になっていないためだ。以下のようなエラーメッセージが帰ってくる。
{
"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 を有効にする。
"build": {
"mac": {
"hardenedRuntime": true
}
}
Hardened Runtime 下では、必要に応じて Entitlement を指定しなければならない。Electron の実行にはallow-unsigned-executable-memory
Entitlement が必要だ。そこで、entitlement.plist
ファイルをbuild
フォルダに作成し、以下のような plist を記述する。
<?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.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
package.json
のentitlements
及びentitlementsInherit
に Entitlement が記述された plist のファイルパスを指定する。
"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: <Bundle ID>
Request Identifier: <UUID>
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 <UUID> -u $APPLE_ID -p $APPLE_PASSWORD
正しく公証が得られている場合は以下のようなメッセージが表示される。おめでとう!
2019-06-05 13:51:18.236 altool[5944:261201] No errors getting notarization info.
RequestUUID: <UUID>
Date: 2019-06-05 04:45:54 +0000
Status: success
LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/<Log file identifier>
Status Code: 0
Status Message: Package Approved