この記事のアップデート版 (2023) は以下にあります。
(この記事は Kilian Valkhof 氏による "Notarizing your Electron application" をご本人の了承を得て日本語訳したものです。)
macOS 10.14.5 のリリース以降、 ”新規の” 開発者によるすべての署名済みアプリケーションは公証を受けなければ Apple のゲートキーパーに引っ掛かり、ユーザーがインストールできないようになっています。
私は、Polypane というアプリケーション(未公証でした)をローンチした2日後に macOS 10.14.5 が登場し、アプリをインストールできない多くのユーザからの苦情に迎えられる結果となりました。
残念なことに、これについては多くの情報が転がっているわけではありません。私が初めてこの問題に遭遇したとき、Google ではそのエラーメッセージすら見つけることができなかったのです!
未公証のアプリを macOS 10.14.5 にインストールしようとすると、つぎのような不吉な予兆をわめくエラーメッセージによる歓迎を受けることになるでしょう:
"Appname.dmg"は、開発元を検証できないため開けません。
このアプリケーションにマルウェアが含まれていないことを検証できません。
Electron アプリケーションの公証に成功する方法
このエラーにどう対処すれば良いのかに 1 週間を費やした結果、公証について大量のことを学び、それに合わせた npm パッケージのアップデート方法も習得できました。
これが Electron アプリケーションの公証に成功するためのガイドです:
- hardened ランタイムを有効にしてアプリをビルドする
- 有効な Developer ID でコード署名する
- アプリの公証に electron-notarize を利用する
- dmg には署名しない
この通り、実際に公証を取得するのはこのうちの1つのステップだけなのですが、アプリの公証に成功するためにはこの4つのステップすべてが必要です。Apple が公証だけでなく、さらにいくつかの必要条件を我々にこっそり課しているからです。
この記事で私が使用するのは electron-builder のバージョン 20.43.0 です。他のビルドツールを使う場合には、これと似通った設定になるでしょうが、そのツールでも同じステップを踏む必要があります。
1. hardened ランタイムを有効にしてアプリをビルドする
Apple が公証に際してこっそり忍ばせたものの1つが hardened ランタイム有効化の要件です。これは基本的にデフォルトではアプリへより少ない権限しか与えないというものです。
これは必ずしも悪くありませんが、もう 1 つ - one more thing - 考慮すべき点があります。
アプリで hardened ランタイムを有効化するためには次の2つが必要です:
- electron-builder の
"mac"
設定で"hardenedRuntime": true
とする - entitlement を正しく設定する
hardened ランタイムを利用するためには、少なくとも 1 つの entitlement(“allow-unsigned-executable-memory”
)を設定することが重要です。
さらにたくさんの entitlement もここで設定できますが、Electron アプリケーションに必要なのはこれ1つだけです。
ビルドフォルダにはこの entitlement を含んだ plist ファイルが置かれていなければなりません。
訳注: electron@12 以降では
allow-unsigned-executable-memory
の指定は不要となりました。
つまりentitlements.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>
electron-builder の"mac"設定で entitlements
と entitlementsInherit
の両方の値にこのファイルを指定してください。
Electron が同一の entitlement に内部的にアクセスするためには entitlementsInherit
が必要となります。
"mac": {
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist"
}
2. 有効な Developer ID でコード署名する
アプリが公証を受けることができるのは有効な Developer ID がインストールされている場合のみです。Electron-builder はキーチェーン内の有効な ID を自動的に使います。
そのためには devloper.apple.com で購入した Apple Developer アカウントが必要で、その ID を Xcode を使ってキーチェーンへダウンロードしておかなければなりません(詳しくは Create, export, and delete signing certificates を参照のこと)。
一方で electron-builder が内部的に利用しているコード署名ツール(electron-osx-sign)は、署名成功の確認のためにサニティ・チェックを行います。これは macOS 10.14.5 より前であれば「正」を返すものですが、10.14.5 ではエラーを返します。なぜならコード署名がうまくいったとしても、そのアプリは(まだ!)公証を受けていないからです。
いまのところ、わたしたちはこのサニティ・チェックを無効化しておく必要があります。そのためには electron-builder の "mac"
設定へ "gatekeeperAssess": false
を追加します。ここまでで "mac"
設定の最小構成はつぎのようになりました:
"mac": {
"hardenedRuntime" : true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist"
}
3. アプリの公証に electron-notarize を利用する
やっと実際の公証ステップです。Electron 開発者(もしくは誰であれなんらかのアプリを持っているひと)ならば Electron-notarize というツールが利用できます。
devDependency として electron-notarize
をインストールしましょう。
このツールは全てのことをおこないます:アプリを zip にして Apple のサーバへアップロード、そして公証が成功するのを待ってアプリの梱包まで。これらはすべて非同期的におこなわれるため、アプリのビルドに要する時間は大幅に増加します(訳注:notary サーバの状態にもよりますが公証だけで3〜10 分ほどかかります)。
アプリはコード署名がなされたあと、dmg へパッケージされる前に公証されなければいけません。Electron-builder では afterSign
というフックが用意されています。これにコード署名のあとで(コード署名されるのを待って)呼び出される JavaScript ファイルをリンクさせることができます。トップレベルの "build"
設定に追加してください:
"build": {
"afterSign": "scripts/notarize.js"
}
afterSign スクリプトはこんな感じです:
require('dotenv').config();
const { notarize } = require('electron-notarize');
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: 'com.yourcompany.yourAppId',
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLEID,
appleIdPassword: process.env.APPLEIDPASS,
});
};
訳注:
2020 年4月現在では上記に加えてascProvider: process.env.ASC_PROVIDER
の指定も必要であるようです。
ASC_PROVIDER
の値には Apple ID に割り振られた Team ID が入ります。Team ID は大文字と数字からなる 10 桁の文字列です。
macOS の場合にのみコード署名されれば良いので、darwin(macOS の内部的名称)環境でなければ return して公証のためのコードを実行しないようにします。
".env"
ファイルから簡便に環境変数を読み込むために dotenv を利用しています。これによりユーザー認証情報をスクリプトへ記載することを避けられます。もし git を利用しているなら、.gitignore
ファイルへ ".env"
を追加するのを忘れないようにしましょう。さらなるセキュリティの強化を望むなら、Safety when using appleIdPassword で概説されている要領で macOS キーチェーンを利用する方法も考慮してみてください。
ユーザー認証情報と言えばもう 1 つ。たしかにこれは Apple ID ではありますが、そのパスワードとしてはアプリ固有のパスワードを設定しておかなければなりません(Apple ID そのもののパスワードを流用しないこと!)。アプリ固有のパスワードは appleid.apple.com で作成できます。
訳注:
Apple ID ではログインに2要素認証が必須であるためスクリプトで認証を受けることができません。
Apple ID 自体のパスワードの代わりにアプリ固有のパスワードで認証を受ける必要があります。
4. dmg には署名しない
バージョン 20.43.0 より前の electron-builder では DMG インストーラにもコード署名がなされていました。これは実際のところ問題ではなかったのですが、あたらしい公証ルールではコード署名されたものには何であろうとすべて公証を受ける必要があります。
しかし、もし DMG インストーラにもコード署名をおこない、公証を受けたとしても実際に起こることはこの記事のトップに挙げたエラーメッセージを引き起こすだけです。個人的にはこれは Apple のロジックのバグ(なにも今にはじまったことではない)だと考えていますが、現実にこうである以上、DMG には署名も公証もしないほうが良いでしょう。
Apple のゲートキーパー・ソフトウェアでは、DMG 内の公証済みアプリを感知してユーザーにそのままインストールを許可しているのではないかと推測されます。
electron-builder@20.43.0 以降では、DMG はデフォルトでは署名されません。また、"dmg"
設定に "sign": false
を追加することで明示的にこの挙動を指定できます。
"dmg": {
"sign": false
}
Enjoy your notarized application!
これらのステップを踏んで .dmg が出来あがれば、それを自分自身へメールで送信したり、アップロード&ダウンロードを繰り返したりしてゲートキーパーの挙動をテストできます(これらは Apple 公式のテスト手続きです)。
すべてがうまくいっていれば、なんの警告も受けることなく .dmg を開けることでしょう。