🍎

[2025年版] Electron(electron-builder)でMacアプリのコード署名と公証を通す

2025/02/10に公開

はじめに

Electronで作ったMacアプリを配布して実行できるようにしたい!
そんなときに避けて通れないのがコード署名(code signing)と公証(notarization)です。初めてで戸惑いながらも先人のおかげで無事に成功できたので、その手順を備忘として記載します。

基本的な流れは以下の2つの記事の通りで、初心者に分かりづらかったポイントに絞って補足記載していきます。
https://qiita.com/sprout2000/items/0fa5db89c43155f4f0c8
https://zenn.dev/portalkeyinc/articles/6e59c5cc3ac7aa

動作環境

  • macOS Sonoma 14.3.1
  • electron@34.0.2
  • electron-builder@25.1.8
  • vite@6.0.11

Developer ID Application証明書を作成

大まかな流れは以下の通りです。

  1. Key Chain Accessから証明書要求を作成
  2. Apple Developer Programに登録
  3. Developer ID Application証明書を作成
  4. 作成された証明書をダウンロードし、ダブルクリックしてKey Chain Accessへインストール
  5. 証明書が有効であることを確認

step1については以下記事が参考になりました。
https://zenn.dev/iwatos/articles/b131cf60d20131513bcb#証明書要求準備

step2,3は冒頭の記事をご参照ください。

step4で私は何度か失敗したのですが、Key Chain Accessにインストールする際、インストール先でiCloudを選択していたのが原因だったようです。ダウンロードした証明書はKey Chain Accessのloginにインストールします。

次にstep5についてです。無事にKey Chain AccessにインストールができるとCertificatesタブに表示されるのですが、私の場合はインストールしただけでは「証明書が信頼できない」との記載があり、赤いマークがついました。このままビルドしたところ、コード署名に失敗しました。

electron-builderを実行した際のログは以下です。

skipped macOS application code signing  reason=cannot find valid "Developer ID Application" identity or custom non-Apple code signing certificate, it could cause some undefined behaviour, e.g. macOS localized description not visible, see https://electron.build/code-signing allIdentities=  1)

以下の記事に従って証明書を有効にすることで、本問題は解決できました。
https://www.gclue.jp/2022/07/trust-certificates-in-keychain.html

ここまででコード署名ができるようになりました。

App Store Connect API Keyの用意

次に公証ですが、冒頭の参考記事に記載の通り、Apple API Keyの取得が推奨されているようですので、取得しにいきます。

私はこの"Apple API Key"がなんのことを指しているのか分からず手間取ったのですが、結論としてApp Store ConnectのTeam Keyを作成すればよいです。手順は@electron/notarize(electron-builderがnotarizationで内部的に利用しているライブラリ)に記載があるのですが、概要は以下のとおりです。

  1. App Store ConnectでTeam Keyを作成する
  2. アクセスとしては App Manager を設定する
  3. 作成したAPI Keyをダウンロードする

次に環境変数にこれらの情報を設定します。

.env
APPLE_API_KEY=step3でダウンロードしたAPI Keyファイルへのフルパス
APPLE_API_KEY_ID=App Store ConnectのキーID
APPLE_API_ISSUER=App Store ConnectのIssuer ID

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

最後にelectron-builderを実行しますが、このとき上記のenvを読んでほしいのでdotenvをインストールしました。またCIで実行することも考え、cross-envも入れておきました。
npm install -D dotenv cross-env

最後にenvを読むようにスクリプトを修正して(cross-env-shell \"node -r dotenv/config $(which electron-builder) --mac --arm64\"の部分)実行。

package.json
...
"dist:mac": "npm run build:electron && npm run build && cross-env-shell \"node -r dotenv/config $(which electron-builder) --mac --arm64\"",
...

何度かKey Chain Accessへのアクセス許可のポップアップが出ましたが、承認して無事にコード署名と公証を成功させることができました。コード署名・公証は初めて行いましたが、5分ほどで完了しました。

$ spctl -a -vvv -t install dist/mac-arm64/xxx.app

dist/mac-arm64/xxx.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: xxxxx (xxxxx)

試しに別端末にdmgを送ってインストールしたところ、無事に実行できました!

GitHub ActionsでCIを組む

ここまでできたらCIもやりたいですよね。証明書とかKeychainとかどうするんだろう。。。と思いましたが、GitHubの公式ドキュメントがありました。

build-mac.yml
name: Build macOS App

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:

jobs:
  build-macos:
    runs-on: macos-latest
    name: Build and Sign macOS App
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Prepare for codesigning
        env:
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          # Create temporary keychain
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # Import certificate to keychain
          echo -n "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12
          security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # Enable codesigning from keychain
          security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # Verify certificate import
          security find-identity -v -p codesigning $KEYCHAIN_PATH

      - name: Setup App Store Connect API key
        env:
          APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY }}
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
        run: |
          # Create private key file from base64
          echo -n "$APPLE_API_KEY_BASE64" | base64 --decode > $RUNNER_TEMP/AuthKey_$APPLE_API_KEY_ID.p8

      - name: Build and Sign macOS app
        env:
          APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
          APPLE_API_KEY: ${{ runner.temp }}/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
        run: |
          npm run build:electron && npm run build
          npx electron-builder --mac --arm64 -p always

APPLE_CERTIFICATEは、Keychain Accessから証明書をエクスポートして、base64 -i /path/to/cert.p12 | pbcopy で取得できます。

また、App Store ConnectのAPI Keyも同様にBASE64エンコードします(APPLE_API_KEY)。これはビルド時にパスを渡す必要があるので、Setup App Store Connect API keyでデコードしてビルド時にenvで渡しています。

署名した出力はelectron-builderの -p always オプションによって設定したホストに書き出されるようになっています。

Discussion