[macOS] Electron アプリの公証に成功する方法 2023

:::note info
この記事は、以下の記事のアップデート版 (2023) です。
:::

https://qiita.com/sprout2000/items/39f25ced2d5ae08f3a68

## まずは前提から

macOS 10.14.5 Mojave 以降では、App Store 経由ではないアプリ（いわゆる野良アプリ）であっても、[コード署名](https://developer.apple.com/jp/support/code-signing/)に加え、Apple 社による「[公証 (notary)](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution)」を受けていない場合には、以下（↓）のような警告が表示され、原則としてそのままでは実行できません。

📸 コード署名がされていない場合:
![](https://storage.googleapis.com/zenn-user-upload/60ccb38906ab-20230606.png)

📸 公証を受けていない場合:
![](https://storage.googleapis.com/zenn-user-upload/64d4216ef69f-20230606.png)

この制限は Electron 製アプリにとっても変わりありません。これをクリアするために必要な条件が以下の 2 つです。

1. **有効な「Developer ID Application 証明書」でコード署名する**
2. **コード署名と公証に [electron-builder](https://www.electron.build/) を利用する**

本稿では electron-builder を利用しますが、他のツールを利用する場合であっても同様の処理を行う必要があります。

:::note warn
以下の記述は、すでに [Xcode](https://apps.apple.com/jp/app/xcode/id497799835) がインストール済みであることを前提としています。
:::

## 有効な Developer ID でコード署名する

アプリへコード署名を施すには、有効な「Developer ID Application 証明書」がその Mac へインストールされている必要があります。そして、electron-builder は自動的に「キーチェーンアクセス.app」内のその証明書を利用します。

📸 キーチェーンアクセスに取り込んだ Developer ID 証明書:
![](https://storage.googleapis.com/zenn-user-upload/9f83ec2afeeb-20230615.png)

そのためには [Apple Devloper Program](https://developer.apple.com/jp/programs/) で購入した Apple Developer アカウントが必要で、その ID の証明書をキーチェーンアクセスへ登録しておかなければなりません。

その手順は：

1. [Apple Devloper Program](https://developer.apple.com/jp/programs/) で開発者アカウントを購入する（年間 99 ドル）
2. そのアカウントの **"Certificates, Identifiers & Profiles"** ページ → **"Certificates"** タブで **Developer ID Application** 証明書を作成
3. その証明書をダウンロードし、ダブルクリックでキーチェーンアクセスへインストール

という流れになります。

📸 開発者アカウントのページ:
![](https://storage.googleapis.com/zenn-user-upload/fbe4f90f6c42-20230606.png)

📸 新しい証明書を作成:
![](https://storage.googleapis.com/zenn-user-upload/0f00e70b619d-20230616.png)

📸 Developer ID Application を選択:
![](https://storage.googleapis.com/zenn-user-upload/00ed46d84280-20230616.png)

📸 作成した証明書のページから Developer ID Application 証明書をダウンロード:
![](https://storage.googleapis.com/zenn-user-upload/d24e98d6b571-20230616.png)

📸 証明書のダブルクリックでキーチェーンアクセスへインストール:
![](https://storage.googleapis.com/zenn-user-upload/f63d0de0d1f3-20230606.png)

## electron-builder でコード署名し、公証を受ける

- electron-builder のインストール

```shell:zsh
npm install --save-dev electron-builder
```

有効な Developer ID Application 証明書がインストールされている場合、[electron-builder](https://www.electron.build/) （厳密には、これに含まれる [@electron/osx-sign](https://github.com/electron/osx-sign) パッケージ）は自動でその証明書を使ってアプリにコード署名を行います。

逆に言えば、あえてコード署名したくない場合には、パッケージスクリプト内の **`mac.identity`** エントリへ _`null`_ を明示的に指定する必要があります。

- パッケージスクリプトの例

```js:builder.js
require("dotenv").config();  // 環境変数の展開（後述）

// パッケージスクリプト本体
require("electron-builder").build({
  config: {
    productName: "Sample",
    artifactName: "${productName}-${version}-${platform}-${arch}.${ext}",
    copyright: "© 2023 sprout2000",
    files: ["dist/**/*"], // 対象アプリの置き場所
    directories: {
      output: "release", // パッケージの出力先
    },
    mac: {
      appId: "jp.sprout2000.Sample",  // アプリID
      category: "public.app-category.developer-tools", // カテゴリ
      target: ["default"], // dmg と zip
      icon: "assets/icon.icns", // アイコンには icns ファイルが必要
      /**
      * 公証の設定（後述）
      */
      notarize: {
        teamId: "369RBS5M6E",
      },
      // identity: null,  // コード署名しない場合はアンコメント
    },
  },
});
```

パッケージスクリプトの詳細については [electron-builder のドキュメント](https://www.electron.build/configuration/configuration)を参照してください。
また、**`mac.category`** エントリの値については [Apple のドキュメント](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8)を参照してください。

### アプリへ公証を受ける

アプリはコード署名がなされたあと、dmg と zip へパッケージされる**前に**公証を受けなければいけません。この間の一連の処理は、electron-builder の **`mac.notarize`** エントリ以下の設定が自動で行ってくれます。

```javascript
    mac: {
      // ...
      notarize: {
        teamId: "369RBS5M6E",
      },
    },
```

- **`teamId`** の値には Apple ID に割り振られた Team ID が入ります。Team ID は大文字と数字からなる 10 桁の文字列です。

### dotenv を導入する

アプリへの公証を受けるためには、electron-builder へ秘匿情報を環境変数として渡す必要があります。しかし、これらは**ビルドスクリプトへベタ書きできないようになっています**。GitHub などへコードをアップロードするとこれらの情報が他者にも晒されてしまうからです。

環境変数を記述した **`.env`** ファイルをスクリプト内で展開してくれるツール、[dotenv](https://github.com/motdotla/dotenv) を導入しましょう。

```shell:sh
npm install --save-dev dotenv
```

### `.env` ファイルを作成する

electron-builder へ渡す環境変数は以下の 2 つです。これらの値を `.env` ファイルへ記述しておきます。

1. APPLE_ID
2. APPLE_APP_SPECIFIC_PASSWORD

- `APPLE_ID` の値は、その名の通り、Developer program に登録したアカウント名（＝メールアドレス）です。
- `APPLE_APP_SPECIFIC_PASSWORD`（＝アプリ固有のパスワード）は、Apple ID そのものの代わりに、そのアプリに固有のパスワードを与えたものです。これは、Apple Developer ID では 2 要素認証が必須とされているため、スクリプトで認証を受けることができないことに由来します。アプリ固有のパスワードは、[appleid.apple.com](https://appleid.apple.com/) で作成します（以下の画像を参照）。

📸 アプリ固有のパスワード作成画面:
![](https://storage.googleapis.com/zenn-user-upload/fe1b373ecf72-20230616.png)

- `.env` ファイルの例

```sh:.env
APPLE_ID="sprout2000@example.jp"
APPLE_APP_SPECIFIC_PASSWORD="hhcd-aemv-kkor-hzes"
```

:::note alert
`.env` ファイルは**必ず `.gitignore` 内にそのパスを指定**してください。
git に管理（＝ GitHub などへアップロード）させてしまうと秘匿情報が漏えいしてしまいます。
:::

- ビルドスクリプトの例 (2)

```diff:builder.js
  // dotenv で ".env" ファイル内の環境変数を展開
+ require("dotenv").config();
  require("electron-builder").build({ /** */ });
```

## コード署名＆公証の実行

パッケージスクリプトを実行し、コード署名と公証のプロセスを開始します。Apple の notary サーバーの状況にもよりますが、全ての処理が完了するまでに 5~10 分の時間を要します。

```shell:sh
node ./builder.js
```

```sh
% node ./builder.js
  • electron-builder  version=24.4.0 os=22.4.0
  • writing effective config  file=release/builder-effective-config.yaml
  • executing @electron/rebuild  arch=arm64 version=24.2.0 appDir=/Users/taro/sample
  • packaging       platform=darwin arch=arm64 electron=24.2.0 appOutDir=release/mac-arm64
  • signing         file=release/mac-arm64/Sample.app identityName=Developer ID Application: Taro Nippon
  • building        target=macOS zip arch=arm64 file=release/Sample-0.1.0-darwin-arm64.zip
  • building        target=DMG arch=arm64 file=release/Sample-0.1.0-darwin-arm64.dmg
  • building block map  blockMapFile=release/Sample-0.1.0-darwin-arm64.dmg.blockmap
  • building block map  blockMapFile=release/Sample-0.1.0-darwin-arm64.zip.blockmap
```

![](https://storage.googleapis.com/zenn-user-upload/843da93d0807-20230510.png)

なお、コード署名＆公証に成功しているか否かは、`spctl` コマンドを用いて確認できます。

```sh
% spctl -a -vvv -t install release/mac-arm64/Sample.app

release/mac-arm64/Sample.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: Taro Nippon (369RBS5M6E)
```

また、いったんクラウドストレージへアップロードした dmg または zip をダウンロードし、自分で開いてみる方法も有効です。その場合は、「インターネットからダウンロードされたアプリケーション」である旨の警告のみが表示されます。

![](https://storage.googleapis.com/zenn-user-upload/578e0d30b2d7-20230606.png)

## おまけ：署名も公証もされていないアプリを開くには？

:::note warn
以下の手順でアプリを開いた結果は自己責任でお願いします。
:::

`xattr` コマンドを利用してアプリを Apple Gate Keeper の対象外とします。

```sh:zsh
% xattr -rc /hoge/foo/Sample.app
```