⛈️

MacOsでのElectronアプリのコード署名と公証に成功するまでの道のり(Electron-builder)

2025/02/07に公開

はじめに

Electronはもともとクロスプラットフォームなビルドをウリの1つとしており、比較的楽に各プラットフォーム向けのビルドを行うことが出来ます。
しかし、ビルドしたElectronをパッケージングした上で配布するには様々な罠が待ち受けています。

今回、MacOSでElectronのビルドと公証を行ったのですがいろんな落とし穴に引っ掛かったので同じような目にあう人を少しでも減らすために記事にしておきます。

また、この記事は以下の記事をベースにさせて頂いています。

https://qiita.com/sprout2000/items/0fa5db89c43155f4f0c8

動作環境

Electron: 32.0.1
electron-builder: 25.0.5
electron-vite: 2.3.0
@electron/notarize: 2.3.2

公証(notary)とは

先ほどの記事からの引用になります。

macOS 10.14.5 Mojave 以降では、App Store 経由ではないアプリ(いわゆる野良アプリ)であっても、コード署名に加え、Apple 社による「公証 (notary)」を受けていない場合には、以下(↓)のような警告が表示され、原則としてそのままでは実行できません。

公証を受けていないアプリケーションは以下のような表示がなされ、開くことが出来ません。

これをクリアするために、MacOS向けのビルドではコード署名と公証を行う必要があります。

Developer ID Application証明書をキーチェーンアクセスへ登録

このセクションについては先ほどの記事そのままでOKです。

  • Apple Developer Programで開発者アカウントを購入する
  • Developer ID Application証明書を作成
  • 証明書をダウンロードし、ダブルクリックでキーチェーンアクセスへインストール

ここまで行ったら、electron-builderの設定に移ります。

electron-builderでコード署名と公証を行う

electron-builderの設定例

以下はelectron-builder.jsonの設定例です。
Apple Team IDは以前はnotarizeに設定していたようですが、現在では環境変数経由で渡すようになっています。

Please specify notarization Team ID in the APPLE_TEAM_ID env var instead of notarize.teamId

notarizeにteamIdを設定してビルドを実行すると上記のような警告が出力されます。

{
  "appId": "xxxxx",
  "productName": "PortalKey",
  "directories": {
    "output": "dist/electron",
    "buildResources": "electron/build"
  },
  "files": ["out/**/*"],
  "extraResources": [
    {
      "from": "electron/build",
      "to": "build"
    }
  ],
  "mac": {
    "icon": "electron/build/portalkey.icns",
    "target": "dmg",
    "target": ["default"],
    "notarize": true
  },
}

electron-builderで認証を行うためにAPIキーを発行する

公証を受けるにはApple APIへの認証が必要ですが、認証には以下の3つのいずれかを設定する必要があります。

  • APPLE_API_KEY, APPLE_API_KEY_ID and APPLE_API_ISSUER.
  • APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID
  • APPLE_KEYCHAIN and APPLE_KEYCHAIN_PROFILE

参考: https://www.electron.build/mac.html#notarize , https://github.com/electron-userland/electron-builder/issues/7859

以前は APPLE_ID および APPLE_APP_SPECIFIC_PASSWORD が推奨されていたようですが、現在ではAPI_KEYが推奨されているようです。
AppleのAPIキーを発行し、環境変数として設定します。

electron-builderの実行

  • Apple Developer ID Application証明書のキーチェーンアクセスへのインストール
  • 環境変数に APPLE_API_KEY / APPLE_API_KEY_ID / APPLE_API_ISSUER を設定

この2つを行ったら electron-builder --mac --config を実行します。
また、electron-builderでは --universal オプションをつけることで Intel / M1 両方で動作するuniversalビルドを行うことが出来ます。
バイナリサイズは大きくなりますが、1つのバイナリで両方のプラットフォームに対応することが出来るため必要に応じてつけるのが良いでしょう。

https://developer.mamezou-tech.com/blogs/2022/04/04/build-intel-m1-electron-app/

electron-builderのビルドが通るまでの落とし穴

上記であっさりビルドが通ればいいのですが、おそらくそうはいきません。
引っ掛かりやすい、かつわかりにくい落とし穴についてエラーと対応策を紹介していきます。

errSecInternalComponentでcodesignに失敗する

electron-builder 実行後、以下のようなエラーメッセージでcodesignに失敗することがあります。

  • Above command failed, retrying 3 more times
  • Above command failed, retrying 2 more times
  • Above command failed, retrying 1 more times
  • Above command failed, retrying 0 more times
  ⨯ Command failed: codesign --sign XXXXXXXXXXXXXX --force --timestamp --options runtime --entitlements /Users/portalkey/work/portalkey-server/frontend/node_modules/app-builder-lib/templates/entitlements.mac.plist /Users/portalkey/work/portalkey-server/frontend/dist/electron/mac-universal/PortalKey.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/af.lproj/locale.pak
(中略)/Resources/af.lproj/locale.pak: replacing existing signature
(中略)/Resources/af.lproj/locale.pak: errSecInternalComponent
  failedTask=build stackTrace=Error: Command failed: codesign --sign XXXXXXXXXXXXXX --force --timestamp --options runtime --entitlements /Users/portalkey/work/portalkey-server/frontend/node_modules/app-builder-lib/templates/entitlements.mac.plist /Users/portalkey/work/portalkey-server/frontend/dist/electron/mac-universal/PortalKey.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/af.lproj/locale.pak

対応1: キーチェーンアクセスをアンロックする

MacOS上でターミナルを起動してビルドを実行している場合ではなく、CIやSSHアクセス経由の場合だとキーチェーンアクセスはロックされています。
Developer ID Application証明書をキーチェーンから取得する際にこのロックが問題となり失敗している可能性が高いため、以下のコマンドでアンロックを行いましょう。

security -v unlock-keychain login.keychain-db

対応2: Developer ID Application証明書がvalidかチェックし、必要ならAppleの中間証明書をインストールする

まずは以下のコマンドで Developer ID Application証明書が認識されているかをチェックしましょう。

~ % security find-identity -v -p codesigning
  1) XXXXXXXXXXXXXXXXXXXXXXXX "Developer ID Application: PortalKey Inc. (YYYYYYYY)"
     1 valid identities found

ここで 0 valid identities と言われる場合、以下が原因の可能性があります。

  • Developer ID Application証明書のインストールが出来ていない
  • Appleの中間証明書がインストールされていない

Developer ID Application 証明書がインストール出来ているにもかかわらず上手く認識されない場合はAppleの中間証明書をインストールしましょう。

https://www.apple.com/certificateauthority/

詳しくは以下の記事が参考になりました。

https://www.gclue.jp/2022/07/trust-certificates-in-keychain.html

ハングアップする

electron-builderの実行後、 signing の実行中に長時間ハングアップすることがあります。
SIGINTで停止した後、以下のようなエラーが表示されます。

  ⨯ Failed to notarize via notarytool.  Failed with unexpected result:
  failedTask=build stackTrace=Error: Failed to notarize via notarytool.
  Failed with unexpected result:
  at /Users/portalkey/work/portalkey-server/frontend/node_modules/@electron/notarize/src/notarytool.ts:92:13

対応1: ひたすら待つ

electron-builderはnotarytoolの呼び出し時に --wait のオプションをつけており、公証に成功するまで待機してくれます。
が、notarizeは以下の条件を満たすとき、長時間(1日~数日)待たされる可能性があるようです。

  • 新規の開発者アカウントである
  • 初めての公証の申請である

週末に実行した場合、休み明けまで通らないこともあるみたいな話もあるらしいです。
さすが天下のApple様ですね

https://github.com/electron/notarize/issues/179
https://developer.apple.com/forums/thread/736977

対応2: 公証のステータスチェックをする

待つといっても公証まで進んでいるかどうかは気になるところです。
公証のステータスをチェックするにはnotarytoolのhistoryコマンドを実行します。

xcrun notarytool history --key ~/path/to/api_key --key-id XXXXXXX --issuer YYYYY
  history
    --------------------------------------------------
    createdDate: 2025-02-07T06:49:51.672Z
    id: fc81be23-66f9-4cd2-8c1a-c5f6bd42dce8
    name: PortalKey.zip
    status: In Progress
    --------------------------------------------------
    createdDate: 2025-02-07T06:15:34.839Z
    id: 5aa899b0-38fb-4013-a9de-ddadb8f819f5
    name: PortalKey.zip
    status: Accepted

In Progress になっている間は公証中であり、公証サーバの状況にもよるようですが一度 Accepted になったことがあれば大体15分以内ぐらいには返ってきます。
ちなみに僕は初回の申請時は In Progress が渋滞していましたが、寝て起きたらすべて Accepted になっていました。

ビルドは成功したが、公証が成功したかわからない

ローカルでパッケージングしたdmgは、公証を必要とせず実行できてしまいます。
そのため、公証に成功したかしていないかわかりません。

対応: spctlコマンドでチェックを行う

一度dmgファイルをGoogle Driveなどの外部ストレージにアップロードし、ダウンロードすることで交渉に成功しているか確認することが出来ます。
が、毎回行うのは面倒なので以下のコマンドでチェックを行います。

spctl --assess -vvv --type install ./dist/electron/mac-universal/PortalKey.app

成功している場合は以下のような出力が得られます。

portalkey@PortalKeynoMac-mini ~ %  spctl -a -vvv -t install ./dist/electron/mac-universal/PortalKey.app
./dist/electron/mac-universal/PortalKey.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: PortalKey Inc. (XXXXXXXX)

失敗している場合は以下のようにrejectedとなります。

portalkey@PortalKeynoMac-mini ~ %  spctl -a -vvv -t install ./dist/electron/mac-universal/PortalKey.app
./dist/electron/mac-universal/PortalKey.app: rejected
source=Unnotarized Developer ID
origin=Developer ID Application: PortalKey Inc. (XXXXXXXX)

最後に

Electronビルドで同じ落とし穴にはまっている人の助けになればという祈りを込めてこの記事を書きました。
少しでも Appleの被害を減らせれば 不幸になる方が少なくなれば幸いです。

PortalKey Tech Blog

Discussion