
バクラク事業部でソフトウェアエンジニアをしている @ta1m1kam です。
最近、Electronでデスクトップアプリケーションを作成する必要がありました。Electronアプリケーションを本番環境で配布する際、コード署名は避けて通れないステップです。しかし、macOSやWindowsのコード署名に関する情報は断片的で、実際の本番環境での実装例はなかなか見つかりません。
そこで本記事では、GitHub Actionsを使ったElectronアプリケーションのコード署名方法について解説します。私自身も多くの方のテックブログに助けられてリリースまで辿り着けたため、同じように課題に直面した方の助けになるよう、コード署名のステップをまとめて記録することにしました。本記事ではビルドツールとしてelectron-builderを使用しています。
なぜコード署名が必要なのか
現在、macOSやWindowsでは未署名アプリをインストールしようとすると、以下のような警告が表示されます。
コード署名は「このアプリを誰が作ったのか」「配布中に改ざんされていないか」をユーザーとOSに示す仕組みです。これにより警告が緩和され、インストール体験をスムーズにすることができます。

macOS編
必要な証明書と準備
macOSでのコード署名に必要なもの:
| 項目 | 説明 | 取得方法 |
|---|---|---|
| Developer ID Application証明書 | アプリケーション署名用の証明書(.p12形式) | Apple Developer Portalから取得 |
| Apple API Key | Notarization用のAPIキー | App Store Connect APIから生成 |
| API Key ID | APIキーの識別子 | APIキー生成時に取得 |
| Issuer ID | 組織の識別子 | App Store Connectで確認 |
Apple Store Connect API Key
electron-builderでは以下の3つの認証方法が利用可能ですが、App Store Connect API Key方式が推奨されています。
利用可能な認証方法:
- App Store Connect API Key (推奨)
- Apple ID + App-specific password
- Keychain profile
App-specific password はApple ID全体へのアクセス権を持ってしまい、メールや連絡先など所属する全チームの情報にアクセスできてしまうため、必要以上に権限が大きくセキュリティリスクがあります。一方、App Store Connect API Keyはスコープを特定のチームに制限でき、個人情報にアクセスすることもないため推奨されています。
詳細は以下のGitHub Issueを確認してみてください。
electron-builder.yml設定(macOS)
ここではdmg形式でのサンプルを記載します。
appId: com.app.your productName: "Your App" mac: hardenedRuntime: true # Notarizationに必須(falseだとエラー) entitlements: build/entitlements.mac.plist entitlementsInherit: build/entitlements.mac.plist dmg: artifactName: YourApp-${version}.${ext}
macキー
hardenedRuntime: Apple 公証 (notarization) に必須。有効にすると macOS の実行時セキュリティ制御(Hardened Runtime)が有効化されるentitlements: 親アプリに付与する権限 (entitlements) の定義ファイル。例: JIT を許可したり、ライブラリ検証を無効にしたりする設定entitlementsInherit: 子プロセス(Electron Helper)に付与する権限の定義ファイル(例ではentitlements と同じファイルを指定している)
dmgキー
artifactName: 出力されるインストーラ(DMG)のファイル名テンプレート
GitHub Actionsでの自動化(macOS)
Github ActionsでmacOS向けにコード署名を行うworkflowのステップ例です。
必要なCSC_LINK(base64エンコードされたp12ファイル), APPLE_API_KEY, APPLE_API_ISSUERなどはGitHub Secretに登録しておきます。
electron-builderを使用している場合は APPLE_API_KEY などの環境変数を自動的に読み取り、コード署名に利用してくれます
jobs: build-app: runs-on: macos-latest steps: - name: Decode and setup certificate for mac env: CSC_LINK: ${{ secrets.csc-link }} APPLE_API_KEY: ${{ secrets.apple-api-key }} run: | WORKSPACE_DIR=$(pwd) # Base64エンコードされた証明書をデコード echo "$CSC_LINK" | base64 --decode > $WORKSPACE_DIR/certificate.p12 echo "CSC_LINK=$WORKSPACE_DIR/certificate.p12" >> $GITHUB_ENV # Apple API Keyをデコード(.p8ファイル) echo "$APPLE_API_KEY" | base64 --decode > $WORKSPACE_DIR/AuthKey.p8 echo "APPLE_API_KEY=$WORKSPACE_DIR/AuthKey.p8" >> $GITHUB_ENV - name: Build mac env: CSC_FOR_PULL_REQUEST: true CSC_KEY_PASSWORD: ${{ secrets.csc-key-password }} # Electron BuilderのNotarization用環境変数 # <https://www.electron.build/mac.html#notarize> APPLE_API_KEY: ${{ env.APPLE_API_KEY }} # .p8ファイルのパス APPLE_API_KEY_ID: ${{ secrets.apple-api-key-id }} # APIキーのID(例:9ABCDEF123) APPLE_API_ISSUER: ${{ secrets.apple-api-issuer }} # Issuer ID(例:12345678-1234-1234-1234-123456789012) run: | VERSION="${{ inputs.electron-app-build-version }}" # macOS用ビルド(Notarizationあり) pnpm build:mac:${{ inputs.env }} -c.extraMetadata.version="${VERSION}" working-directory: ./apps/${{ inputs.app-name }}
Windows編
Windowsのコード署名には、コード署名証明書を提供するベンダーとの契約が必要です。 現在、Windows向けのデスクトップアプリケーション配布にはEV署名が求められますが、ほとんどのベンダーは物理キー(USB)による提供のみでした。そこで私は DigiCert KeyLocker を利用しました。KeyLockerはクラウドベースでのEV署名をサポートしており、安全にコード署名を行えるうえ、CI/CDでの実装ドキュメントも充実しています。 knowledge.digicert.com
AzureのTrusted Signingも検討しましたが、当時はUSまたはCanadaのみのサポートだったため断念しました。
Trusted Signing Public Preview Update | Microsoft Community Hub
必要な証明書と準備
WindowsでのDigiCert KeyLocker署名に必要なもの(DigiCert KeyLocker GitHub Actions)
DigiCertから取得する必要があるアクセスキーや証明書の発行の仕方はもドキュメントに書いてあります。
| 項目 | 説明 | 取得方法 |
|---|---|---|
| SM API Key | KeyLocker APIアクセスキー | DigiCert Portalで生成 |
| クライアント証明書 | API認証用証明書(.p12形式) | DigiCert Portalからダウンロード |
| 署名証明書のSHA1ハッシュ | 証明書のフィンガープリント | DigiCert Portalで確認 |
electron-builder.yml設定(Windows)
ここではnsis形式でのサンプルを記載します。
appId: com.app.your productName: "Your App" afterAllArtifactBuild: scripts/afterAllArtifactBuild.js # 署名フック win: executableName: Your-App nsis: artifactName: Your-App-${version}-setup.${ext} shortcutName: ${productName} uninstallDisplayName: ${productName}
afterAllArtifactBuild: ビルド完了後にカスタム処理(署名やアップロード)をするフック
winキー
executableName: Windows 実行ファイル (.exe) 名
nsisキー
artifactName: インストーラー (.exe) ファイル名shortcutName: ショートカットの表示名uninstallDisplayName: アンインストーラ表示名
afterAllArtifactBuildフック実装
Windows環境で生成された setup.exe を対象に signtool.exe を用いてコード署名を行います。環境変数で指定された証明書情報を使って署名し、その後 signtool verify によって署名を検証しています。
const { execSync } = require("node:child_process"); const fs = require("node:fs"); const path = require("node:path"); exports.default = async function afterAllArtifactBuild(context) { const { artifactPaths, outDir } = context; console.log("afterAllArtifactBuild called"); console.log(`Process platform: ${process.platform}`); // Windowsの場合のみ実行 if (process.platform !== "win32") { console.log("Skipping code signing - not Windows"); return; } console.log("Starting Windows setup.exe code signing process..."); // 環境変数の確認 const requiredEnvVars = [ "SM_HOST", "SM_API_KEY", "SM_CLIENT_CERT_FILE", "SM_CLIENT_CERT_PASSWORD", "SM_CODE_SIGNING_CERT_SHA1_HASH", ]; for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { throw new Error(`Required environment variable ${envVar} is not set`); } } // setup.exeファイルを検索 const setupFiles = artifactPaths.filter((filePath) => { const fileName = path.basename(filePath); return ( fileName.includes("setup") && fileName.endsWith(".exe") && !fileName.endsWith(".blockmap") ); }); if (setupFiles.length === 0) { console.log("No setup.exe files found in artifacts"); return; } for (const filePath of setupFiles) { const fileStats = fs.statSync(filePath); console.log(`Signing file: ${filePath} (size: ${fileStats.size} bytes)`); try { // signtoolでコード署名を実行 // DigiCertガイド推奨のパラメータを使用 // 参考: <https://docs.digicert.com/ja/digicert-keylocker/sign-with-digicert-signing-tools/sign-with-signtool.html> const signCommand = [ "signtool.exe", "sign", `/sha1 ${process.env.SM_CODE_SIGNING_CERT_SHA1_HASH}`, // 証明書の指定 "/tr <http://timestamp.digicert.com>", // タイムスタンプサーバー "/td SHA256", // タイムスタンプのダイジェストアルゴリズム "/fd SHA256", // ファイルのダイジェストアルゴリズム `"${filePath}"`, ].join(" "); console.log(`Executing: ${signCommand}`); execSync(signCommand, { stdio: "inherit" }); // 署名の検証 const verifyCommand = [ "signtool.exe", "verify", "/v", // 詳細出力 "/pa", // Authenticodeの検証 `"${filePath}"`, ].join(" "); console.log(`Verifying: ${verifyCommand}`); execSync(verifyCommand, { stdio: "inherit" }); console.log(`Successfully signed: ${path.basename(filePath)}`); } catch (err) { console.error(`Failed to sign ${path.basename(filePath)}:`, err.message); throw err; } } console.log("Windows setup.exe code signing completed successfully"); };
GitHub Actionsでの自動化(Windows)
Github ActionsでWindows向けにコード署名を行うworkflowのステップ例です。
Windows環境でElectronアプリをビルドする際、DigiCert KeyLockerを使った署名環境を準備し、秘密情報から証明書を復号・セットアップします。その後、DigiCertのKeyLocker Toolsをインストール・同期し、環境変数を設定してelectron-builderでWindows用インストーラーをビルドします。実際の署名処理は先ほどの afterAllArtifactBuild.js で実行されます。
jobs: build-app: runs-on: windows-latest steps: - name: Decode and setup certificate for windows shell: bash run: | # Base64エンコードされたクライアント証明書をデコード echo "${{ secrets.sm-client-cert-file-base64 }}" | base64 --decode > D:/Certificate_pkcs12.p12 - name: Set variables for windows shell: bash run: | echo "KEYPAIR_NAME=gt-standard-keypair" >> $GITHUB_OUTPUT echo "CERTIFICATE_NAME=gt-certificate" >> $GITHUB_OUTPUT echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH - name: Setup SSM KSP on windows latest shell: cmd run: | # DigiCert KeyLocker Toolsをダウンロード(KSP、PKCS11、smctlを含む) # 参考: <https://docs.digicert.com/ja/digicert-keylocker/ci-cd-integrations-and-deployment-pipelines/scripts/github.html> curl -X GET <https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download> ^ -H "x-api-key:${{ secrets.sm-api-key }}" ^ -o Keylockertools-windows-x64.msi # インストール msiexec /i Keylockertools-windows-x64.msi /quiet /qn # KSP(Key Storage Provider)の登録と確認 smctl windows ksp register smctl windows ksp list smctl.exe keypair ls # 証明書の同期 smctl windows certsync - name: Build win shell: bash env: # Electron Builderでの自動署名を無効化 CSC_IDENTITY_AUTO_DISCOVERY: false # DigiCert KeyLocker環境変数 SM_HOST: ${{ secrets.sm-host }} SM_API_KEY: ${{ secrets.sm-api-key }} SM_CLIENT_CERT_FILE: ${{ env.SM_CLIENT_CERT_FILE }} SM_CLIENT_CERT_PASSWORD: ${{ secrets.sm-client-cert-password }} SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.sm-code-signing-cert-sha1-hash }} run: | VERSION="${{ inputs.electron-app-build-version }}" # Windows用ビルド(afterSignフックで署名) pnpm build:win:${{ inputs.env }} -c.extraMetadata.version="${VERSION}" working-directory: ./apps/${{ inputs.app-name }}
まとめ
本記事では、Electronアプリケーションのコード署名をmacOSとWindows両方の環境で実装する方法を解説しました。 ニッチな内容ですが、Electronアプリケーションのコード署名で悩んでいる方の参考になれば幸いです。
今回の記事を読んで気になった方やLayerXについて知りたい方は、ぜひカジュアル面談でおしゃべりしましょう!
採用情報についてはこちらです。 jobs.layerx.co.jp