Electron is one of the most widely used frameworks for building cross-platform desktop applications, with VS Code, Slack, Discord, Figma, and 1Password all built on it. When you ship an Electron app, you are shipping for Windows and macOS simultaneously, and each platform has its own independent signing requirement with its own certificate type from its own issuer.
The Windows signing requirement and the macOS signing requirement cannot be satisfied by the same certificate. They use different certificate authorities, different certificate types, and different signing tools. Getting both right in a CI/CD pipeline adds configuration complexity that the official documentation can be difficult to navigate.
This guide covers exactly which certificate you need for each platform, how electron-builder handles signing for each, how to configure a CI/CD pipeline for both simultaneously, and the specific challenge of automating macOS notarization.
The Two Independent Signing Requirements
| Platform | Certificate type | Issued by | Purpose | What happens without it |
| Windows | OV code signing certificate (standard for most apps); EV required only for kernel drivers | Public CA (SSL.com, DigiCert, Sectigo, GlobalSign, etc.) | Authenticode signature: removes Unknown Publisher UAC warning; builds SmartScreen reputation | ‘Unknown Publisher’ in UAC dialog; SmartScreen blocks on first download; enterprise policies may block installation |
| macOS | Developer ID Application certificate | Apple Developer Program ($99/year) | Gatekeeper accepts app without warning; required alongside notarization for macOS 10.15+ | Gatekeeper blocks the app with ‘app is from unidentified developer’ warning; cannot run without user override |
Electron apps are Chromium + Node.js packaged into a native binary. On Windows, Windows treats the packaged .exe as any other native executable subject to Authenticode and SmartScreen evaluation. On macOS, the packaged .app bundle is subject to Gatekeeper and notarization. Both systems evaluate the app independently using their own certificate infrastructure. There is no cross-platform certificate that satisfies both.
Windows Signing: OV Code Signing Certificate
For Windows, you need an OV (Organization Validation) code signing certificate from a public CA whose root is in the Windows Certificate Trust Store. EV (Extended Validation) is only required if your Electron app includes kernel-mode components, which almost no Electron app does. For standard Electron applications, OV is sufficient.
Choosing a CA for your Windows certificate
Any CA with roots in the Windows trust store works. Well-established CAs with strong pipeline integration support for Electron developers include SSL.com (which offers both cloud HSM signing via eSigner and traditional certificate delivery), DigiCert (KeyLocker for cloud HSM), Sectigo, and GlobalSign. All major CAs issue certificates valid for up to 460 days following the CA/B Forum’s March 2026 validity reduction.
The most significant operational difference between CAs for Electron pipeline use is whether they offer cloud HSM signing. If you use a physical hardware token for signing, that token cannot be plugged into a GitHub Actions runner or other cloud CI. Cloud HSM services store the private key remotely and sign via API, which is compatible with any CI/CD environment. SSL.com’s eSigner, DigiCert’s KeyLocker, and Sectigo’s cloud signing are all designed for this use case.
electron-builder Windows signing configuration
electron-builder reads signing credentials from environment variables. The standard variables for a PKCS12 certificate file are CSC_LINK (path to the .p12/.pfx file or a base64-encoded certificate) and CSC_KEY_PASSWORD (the certificate’s password).
| // package.json build configuration for electron-builder Windows signing:
{ “build”: { “win”: { “target”: [“nsis”], “certificateFile”: “path/to/cert.p12”, “certificatePassword”: “your_cert_password” // OR use environment variables (preferred for CI): // electron-builder reads CSC_LINK and CSC_KEY_PASSWORD automatically } } }
# Set environment variables in CI pipeline (GitHub Actions example): # CSC_LINK: base64-encoded .p12 file content or path # CSC_KEY_PASSWORD: certificate password # To base64-encode a .p12 file: $ base64 -w 0 certificate.p12 > cert_base64.txt # Store the output as a CI secret, then in the workflow: export CSC_LINK=”$(echo $CERTIFICATE_P12 | base64 –decode)” # Or provide as a file path if the file is written to disk first. |
Never commit your .p12 certificate file or its password to a repository. Store them as encrypted CI secrets (GitHub Actions secrets, GitLab CI/CD variables, etc.). The certificate’s private key must be protected: anyone with access to the .p12 and password can sign arbitrary files with your publisher identity.
Cloud HSM signing for Windows in CI/CD
If you use a cloud HSM service instead of a certificate file, the signing approach changes. Cloud HSM services typically provide a client library or signing tool that replaces signtool.exe and handles the signing operation against the remote HSM. For electron-builder, this typically involves configuring a custom sign hook rather than the standard CSC_ environment variables.
SSL.com’s eSigner, for example, provides an eSigner CodeSignTool that electron-builder can invoke. DigiCert KeyLocker provides a similar mechanism. The cloud HSM service’s documentation covers the specific integration steps for electron-builder. The core principle is the same: the private key stays in the HSM, signing operations are authenticated API calls.
macOS Signing: Developer ID Application Certificate + Notarization
macOS signing for Electron apps has two mandatory steps: code signing with a Developer ID Application certificate, then notarization through Apple’s notarization service. Both are required for Gatekeeper to accept the app without warnings on macOS 10.15 and later.
Step 1: Developer ID Application certificate
The Developer ID Application certificate is issued through the Apple Developer Program. You cannot purchase this from a third-party CA. It requires a $99/year Apple Developer Program membership and is generated or issued through the Apple Developer portal or through Xcode.
The certificate is stored in the macOS Keychain on the machine where it was created, and exported as a .p12 file (with a password you set) for use in CI environments. The .p12 contains both the certificate and the private key, and must be imported into the Keychain on the CI machine before signing.
Step 2: Hardened Runtime
macOS notarization requires that the app is built with Hardened Runtime enabled. Hardened Runtime restricts the runtime capabilities of the app and enables additional security protections. In electron-builder, this is set in the mac configuration: hardenedRuntime: true. The app also needs an entitlements file that grants specific permissions the Electron framework requires (JIT compilation, for example).
| // package.json electron-builder mac signing configuration:
{ “build”: { “mac”: { “type”: “distribution”, “hardenedRuntime”: true, “gatekeeperAssess”: false, “entitlements”: “assets/entitlements.mac.plist”, “entitlementsInherit”: “assets/entitlements.mac.plist” } } }
# Minimum entitlements.mac.plist for Electron apps: # <?xml version=”1.0″ encoding=”UTF-8″?> # <!DOCTYPE plist PUBLIC … > # <plist version=”1.0″> # <dict> #Â Â <key>com.apple.security.cs.allow-jit</key><true/> #Â Â <key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/> # </dict> # </plist>
# Required environment variables for macOS signing in CI: # CSC_LINK: base64-encoded Developer ID Application certificate .p12 # CSC_KEY_PASSWORD: .p12 password # APPLE_ID: Apple ID email used for notarization # APPLE_APP_SPECIFIC_PASSWORD: App-specific password from appleid.apple.com # APPLE_TEAM_ID: 10-character team ID from developer.apple.com |
Step 3: Notarization
After electron-builder signs the app, it must be submitted to Apple’s notarization service, which scans the app for malware and verifies signing requirements. Apple returns a notarization ticket that must be stapled to the app before distribution. electron-builder can handle notarization automatically via the afterSign hook or by enabling notarize in the mac build configuration.
Notarization requires Apple ID credentials (email and an app-specific password generated at appleid.apple.com) or an App Store Connect API key. The API key approach is more reliable in CI environments because app-specific passwords can be blocked by Apple’s security systems when used from unusual IP addresses (like cloud CI runners).
App Store Connect API keys are created in App Store Connect under Users and Access, Keys. They require a .p8 file (downloaded once, cannot be re-downloaded), a Key ID, and the Issuer ID from the same page. These are the recommended credentials for CI-based notarization.
| // afterSign hook for notarization (afterSign.js):
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; const appPath = `${appOutDir}/${appName}.app`;
// Using App Store Connect API key (recommended for CI): return await notarize({ appPath, appleApiKey: process.env.APPLE_API_KEY,     // path to .p8 file appleApiKeyId: process.env.APPLE_API_KEY_ID, appleApiIssuer: process.env.APPLE_API_ISSUER, }); };
// Register the hook in electron-builder config: // “afterSign”: “afterSign.js” |
Running Both Platforms in CI: GitHub Actions Example Structure
Most Electron CI/CD pipelines build Windows on Windows runners and macOS on macOS runners. The signing credentials for each platform are separate CI secrets. The key secrets needed:
| Secret name | Platform | Content | Notes |
| WINDOWS_CERT_P12 | Windows | Base64-encoded .p12 certificate file | From your Windows OV code signing certificate |
| WINDOWS_CERT_PASSWORD | Windows | Password for the .p12 file | Set when exporting the certificate |
| MAC_CERT_P12 | macOS | Base64-encoded Developer ID Application .p12 | Exported from Keychain with password |
| MAC_CERT_PASSWORD | macOS | Password for the macOS .p12 file | Set when exporting from Keychain |
| APPLE_API_KEY | macOS notarization | Contents of the .p8 API key file | From App Store Connect; download once |
| APPLE_API_KEY_ID | macOS notarization | Key ID from App Store Connect | 10-character string |
| APPLE_API_ISSUER | macOS notarization | Issuer ID from App Store Connect | UUID format |
| APPLE_TEAM_ID | macOS signing | 10-character Developer Team ID | From developer.apple.com Membership |
The macOS signing certificate and notarization credentials must be used on a macOS runner. GitHub Actions provides macOS runners (macos-latest or specific versions). Apple’s signing tools and notarization infrastructure only function on macOS. Cross-platform signing for macOS from Linux or Windows is not natively supported by electron-builder, though jsign with Azure Trusted Signing can sign Windows .exe files from any platform.
Certificate Renewal and the 460-Day Validity Change
Starting March 2026, Windows OV code signing certificates are valid for a maximum of 460 days (approximately 15 months). Previously, certificates could be valid for up to 3 years. For Electron developers, this means the Windows signing certificate stored as a CI secret needs to be rotated approximately annually rather than every 3 years.
Operationally: the CSC_LINK (or equivalent base64-encoded certificate) stored as a CI secret will need to be updated with a new certificate when the old one expires. Set a calendar reminder 60-90 days before the certificate expiry date. When you renew the certificate, export the new .p12, re-encode it as base64, update the CI secret, and update the CSC_KEY_PASSWORD secret if the password changes.
The macOS Developer ID certificate is managed through Apple and has its own validity period (typically 5 years). Its renewal is separate from the Windows certificate renewal.
Frequently Asked Questions
Do I need an EV certificate for my Electron app on Windows?
For most Electron applications, no. EV code signing certificates are required specifically for Windows kernel-mode driver signing and for establishing a Microsoft Hardware Dev Center account. Standard Electron apps do not include kernel-mode components and do not require EV. An OV code signing certificate from any major public CA (SSL.com, DigiCert, Sectigo, GlobalSign, etc.) is sufficient for Windows Electron app signing, removes the Unknown Publisher UAC warning, and builds SmartScreen reputation in the same way that EV does post-August 2024 (when Microsoft removed EV’s SmartScreen instant reputation bypass).
Can I sign my Electron app on a CI server with a hardware token?
A physical hardware USB token cannot be plugged into a cloud CI runner. For CI/CD automated signing on Windows, you have two options: export the certificate as a .p12 file (most OV certificates allow this; the private key is exported in the file), or use a cloud HSM signing service that provides API-based signing without a physical token. The .p12 file approach is simpler to set up; cloud HSM is more secure because the private key never exists as a file. For macOS signing, the certificate must be imported into the macOS Keychain on the macOS CI runner, which electron-builder handles through the CSC_ environment variables.
My macOS Electron app passes signing but fails notarization. What is wrong?
The most common notarization failures for Electron apps are: Hardened Runtime not enabled (set hardenedRuntime: true in electron-builder’s mac configuration), missing entitlements for capabilities Electron requires (JIT compilation is the most common; add com.apple.security.cs.allow-jit to the entitlements.plist), using an Apple ID password directly instead of an app-specific password for authentication (generate one at appleid.apple.com under Security), or using an old version of @electron/notarize that uses a deprecated API. Update electron-builder and @electron/notarize to their latest versions and check the notarization output for the specific rejection reason from Apple’s notarization service.
How does the 460-day code signing certificate validity affect my Electron build pipeline?
The Windows OV code signing certificate stored as a CI secret will now expire approximately every 15 months instead of every 3 years. Set up calendar reminders 60-90 days before your certificate’s expiry date. When the certificate expires, your CI signing step will fail. To renew: order a new certificate from your CA, export it as a .p12 file, re-encode as base64, and update the CI secret (CSC_LINK and CSC_KEY_PASSWORD). The macOS Developer ID certificate has a separate validity period managed by Apple.
