Code Signing for Windows
Windows code signing is supported and runs automatically once you configure a signing method — or simply provide signing credentials through environment variables. electron-builder signs every executable and installer it produces so that Windows SmartScreen and your auto-updater can verify your app's publisher and confirm it hasn't been tampered with.
By default each binary is dual-signed with both SHA-1 and SHA-256 (signingHashAlgorithms: ["sha1", "sha256"]) so it validates on legacy as well as modern Windows. A few targets are fixed: .msi holds a single signature, which is SHA-256 unless sha1 is the only algorithm listed, and AppX/MSIX is SHA-256 only.
Choosing a signing method
electron-builder offers several signing backends, selected by the type field of win.sign (detailed in the table below). Pick based on where your private key lives and which OS your build runs on:
- Certificate file or Windows store (
signtool, the default) — a.pfx/.p12file or a certificate already in the Windows certificate store. Signs with Microsoftsigntool.exeon Windows andosslsigncodeon macOS/Linux. - Hardware Security Module / FIPS token (
hsm) — the key stays in hardware, accessed through a Windows cryptographic service provider. Windows only. - PKCS#11 hardware token (
pkcs11) — a smart card, USB HSM, or network token reached through a PKCS#11 module. Runs on macOS/Linux viaosslsigncode. - Azure Trusted Signing (
azure) — Microsoft's cloud signing service; no local key to manage. Works on any OS.
OV vs. EV certificates
For file- or store-based signing you need a Windows code signing certificate from a CA (DigiCert, Sectigo, SSL.com, …). They come in two grades:
- OV (Organization Validation) — the common, lower-cost option. New publishers see a SmartScreen "unknown publisher" warning during install that fades as your download reputation grows. OV certificates export to a
.pfx, so they work in CI with thesigntoolmethod. - EV (Extended Validation) — since 2024 no longer earns SmartScreen reputation immediately; Microsoft treats EV and OV code signing certificates the same for SmartScreen (details). Its private key is bound to a hardware token and cannot be exported to a file. Identify an EV certificate by
certificateSubjectNameorcertificateSha1rather than a file path, and sign with thehsm(Windows) orpkcs11(macOS/Linux) method — or switch toazureto avoid managing hardware at all.
Both grades work with auto-update.
You don't need Windows to sign a Windows app. The default signtool method signs file-based certificates with osslsigncode on macOS/Linux, and the pkcs11 and azure methods are designed for non-Windows CI. See also Code Signing Windows Apps on Unix.
Unified win.sign key
All Windows code signing is configured under a single win.sign key using a discriminated union with an explicit type field:
win.sign value | Behavior | Platforms |
|---|---|---|
| unset | Sign using credentials discovered from the environment — WIN_CSC_LINK / CSC_LINK with WIN_CSC_KEY_PASSWORD / CSC_KEY_PASSWORD | all |
{ type: "signtool", ... } | Local .pfx/.p12 certificate file or Windows certificate store | Windows (signtool.exe) · macOS/Linux (osslsigncode) |
{ type: "hsm", ... } | Hardware Security Module / FIPS token via a Windows CSP — Beta | Windows only |
{ type: "pkcs11", ... } | PKCS#11 hardware token via osslsigncode — Beta | macOS/Linux |
{ type: "azure", ... } | Azure Trusted Signing cloud service — Beta | all (signtool /dlib, Wine on macOS/Linux) |
false or null | Disable signing (resedit resource editing still runs). No certificate is read, not even from WIN_CSC_LINK / CSC_LINK, so no publisherName is derived: a build that writes app-update.yml then fails unless you also set win.verifyUpdateCodeSignature: false (the update-verification opt-out, written into app-update.yml). If you sign the build outside electron-builder, use { type: "signtool", publisherName: [...] } (or a custom sign hook with publisherName) instead, so app-update.yml carries the publisher electron-updater verifies updates against | all |
Combining win.sign: false with forceCodeSigning: true is a configuration error and fails the build, also for a prepackaged app. With forceCodeSigning, every file a signing step reports as signed is read back and must carry a valid Authenticode signature — so a custom sign hook must sign the file at configuration.path in place — and the executable of a prepackaged app, which electron-builder does not sign, must already be signed.
Publisher name and update verification
When your app uses electron-updater, the resolved publisherName (your explicit win.sign.publisherName, or CN=<common name> of the certificate) is written into app-update.yml, and electron-updater refuses to install a downloaded NSIS update whose Authenticode signer does not match it. To catch a wrong publisherName before you ship, electron-builder checks the signed installer itself at build time — the nsis installer, or the nsis-web stub — with the same matching rules electron-updater applies, and fails the build if the signer does not match. Because it inspects the signed output, this also covers signing routes where the certificate is not known up front: a custom sign hook, Azure Trusted Signing and PKCS#11.
- Each
publisherNameentry must be a distinguished name (DN) of the certificate's subject (e.g.CN=My Company, O=My Company, L=Berlin, S=Berlin, C=DE). It must match every attribute it lists; attributes it leaves out are not compared, and attribute names are the ones Windows shows in the certificate subject (for exampleSfor the state or province, notST). - A bare name (
My Company) fails the build since v27 — electron-updater 7 never matches one. See the v27 migration guide. - Without
publisherName, the derived value isCN=<common name>of the certificate, quoted the way Windows quotes it in the subject. Only the CN is compared, so a renewed certificate with the same CN keeps updates working even when its other subject attributes change. For a stricter pin, set a fuller DN explicitly; a renewal that changes an attribute you configured then needs a release, signed with the old certificate, that lists both DNs before you switch (see key rotation). - The signer is read with
Get-AuthenticodeSignatureon Windows (a signature it cannot read fails the build) and from the installer's embedded signature on macOS/Linux (a signature that cannot be parsed there is only warned about). - Without any
publisherName—win.sign: false/null, no certificate, orpublisherName: null— a build that writesapp-update.ymlfails withInvalidConfigurationErrorsince v27, because electron-updater 7 rejects every update of an app whoseapp-update.ymlhas none.win.verifyUpdateCodeSignature: falseis the only opt-out: the build then writesverifyUpdateCodeSignature: falseinstead of apublisherName, and electron-updater installs updates without verifying their signature. See the v27 migration guide. - electron-updater installs an update only when PowerShell (
Get-AuthenticodeSignature) verifies it. When PowerShell cannot be started (for example because it is blocked by AppLocker), fails, times out or reports a signature from a Windows security catalog instead of one embedded in the installer, the update is rejected (a catalog-signed installer can be accepted with the opt-inautoUpdater.allowCatalogSignature = true, or per release withwin.allowCatalogSignature: truein a signed update manifest, when the catalog signer matchespublisherName). Where PowerShell may not run on your users' machines, plug in a native verifier in your first release built with electron-updater 7. See the v27 migration guide. - The check runs only when the installer was signed by electron-builder (or your
signhook),win.verifyUpdateCodeSignatureis enabled andapp-update.ymlcarries apublisherName. Setwin.verifyUpdateCodeSignature: falseonly if your updates are not Authenticode-signed. Signing that happens after electron-builder finishes is not checked.
Signtool Signing (type: "signtool")
The standard signing mode, and the default when win.sign is unset. Signs with a local certificate file (.pfx/.p12) or a certificate from the Windows certificate store, using Microsoft signtool.exe on Windows and osslsigncode on macOS/Linux.
Setup
Set environment variables (recommended for CI):
WIN_CSC_LINK=/path/to/cert.pfx # or base64-encoded file contents
WIN_CSC_KEY_PASSWORD=your-cert-password
Or specify the certificate directly in config:
{
"win": {
"sign": {
"type": "signtool",
"certificateFile": "cert.pfx",
"certificatePassword": "...",
"signingHashAlgorithms": ["sha256"]
}
}
}
For EV certificates (bound to a USB dongle), identify by subject name instead:
{
"win": {
"sign": {
"type": "signtool",
"certificateSubjectName": "My Company, Inc."
}
}
}
Execution flow
- The certificate is resolved from
certificateFile(or theWIN_CSC_LINK/CSC_LINKenv var), or looked up in the Windows certificate store bycertificateSubjectName/certificateSha1. - Each file is signed once per entry in
signingHashAlgorithms— by default SHA-1, then SHA-256 with the second signature appended as a nested signature. - On Windows the signer is Microsoft
signtool.exe; on macOS/Linux it isosslsigncodefrom thewinCodeSigntoolset bundle — no Wine or Windows VM is involved. Note: certificate-store lookups (certificateSubjectName/certificateSha1) read the Windows store and therefore require Windows (or the bundled Windows VM), whereas file-based signing works on any platform.
Certificate-store lookups and the legacy Azure Trusted Signing integration run PowerShell, which electron-builder starts by absolute path and never looks up on PATH; toolsets.executables (powershell, pwsh) sets other locations. On Windows it uses PowerShell 7 from %ProgramFiles%\PowerShell\7\pwsh.exe, the install location of its MSI installer, and otherwise Windows PowerShell from %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe; SystemRoot and ProgramFiles must be drive-absolute paths such as C:\Windows. Inside a Parallels VM it uses Windows PowerShell from C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe, the location of a default Windows installation; PowerShell 7 is not used there, because prlctl exec cannot start a path that contains a space. On macOS and Linux it uses pwsh from the install location of Microsoft's package: /usr/local/microsoft/powershell/7/pwsh on macOS, /opt/microsoft/powershell/7/pwsh or /snap/bin/pwsh on Linux.
Configuration reference
Interface: WindowsSigntoolSigningConfig
Sign with a certificate file (.pfx / .p12) or with a certificate from the Windows
certificate store, using Microsoft signtool.exe on Windows and osslsigncode on macOS/Linux.
This is the default mode when win.sign is unset. Supply the certificate one of four ways:
a file via certificateFile (or the WIN_CSC_LINK env var), or a store lookup via
certificateSubjectName or certificateSha1.
Extends
WindowsSigningSharedOptions
Properties
additionalCertificateFile?
readonlyoptionaladditionalCertificateFile?:string|null
Path to an additional certificate file (typically an intermediate / cross-signing CA
certificate) whose contents are added to the signature block via signtool's /ac flag. Use
this when the signing certificate's full chain is not already present on target machines and
you want it embedded in the signature so the chain can be validated.
Inherited from
WindowsSigningSharedOptions.additionalCertificateFile
certificateFile?
readonlyoptionalcertificateFile?:string|null
Path to the PKCS#12 certificate file (.pfx or .p12) holding both the signing certificate
and its private key. Passed to signtool via /f (or osslsigncode via -pkcs12).
Prefer supplying this out-of-band via the WIN_CSC_LINK (or CSC_LINK) environment variable
instead of hardcoding a path — that variable also accepts an https:// URL or a base64-encoded
certificate, which is safer and more convenient on CI. See
Code Signing.
certificatePassword?
readonlyoptionalcertificatePassword?:string|null
The password protecting the private key in certificateFile. Passed to signtool via /p
(or osslsigncode via -pass).
Prefer the WIN_CSC_KEY_PASSWORD (or CSC_KEY_PASSWORD) environment variable so the secret
never lands in your build configuration.
certificateSha1?
readonlyoptionalcertificateSha1?:string|null
Select the signing certificate from the Windows certificate store by its SHA-1 thumbprint,
instead of pointing at a .pfx file. electron-builder looks the certificate up in the store
and signs with /sha1 <thumbprint> /s <store>. Like
certificateSubjectName, this suits EV certificates backed by a
hardware token; if both are set, both must resolve to the same certificate.
Works only on Windows (or macOS with Parallels Desktop, via the bundled Windows VM).
certificateSubjectName?
readonlyoptionalcertificateSubjectName?:string|null
Select the signing certificate from the Windows certificate store by (a substring of) its
subject name, instead of pointing at a .pfx file. electron-builder enumerates the installed
code-signing certificates, picks the match, and signs by its thumbprint
(/sha1 <thumbprint> /s <store>). Required for EV (Extended Validation) certificates,
whose private keys live on a hardware token and cannot be exported to a file.
Works only on Windows (or macOS with Parallels Desktop, via the bundled Windows VM).
publisherName?
readonlyoptionalpublisherName?:string|string[] |null
The publisher name(s) to associate with the signature: a Distinguished Name (DN) of your code
signing certificate's subject, written with the attribute names Windows uses (e.g.
CN=My Company, O=My Company, C=US). Every attribute you list must equal the certificate's;
attributes you leave out are not compared. A bare name (My Company) fails the build since v27,
because electron-updater 7 never matches it — see
the migration guide.
May be a single string or an array — an array is useful when rotating certificates, so updates
signed by either the old or the new certificate still verify.
This value is not a signtool argument; it is consumed in two places:
- Update verification — when verifyUpdateCodeSignature is
enabled, it is written into
app-update.ymland electron-updater checks it against the certificate that signed each downloaded update. - AppX / MSIX identity — the package
Publisherattribute must equal the certificate subject, so electron-builder derives it from this value (or the certificate) to keep them in sync; a mismatch makes packaging fail withERROR_BAD_FORMAT.
Defaults to CN=<common name> of your code signing certificate, so a renewed certificate with the
same common name keeps updates working even when its other subject attributes change. For a stricter
pin, set a fuller DN explicitly (a renewal that changes a configured attribute then needs a release,
signed with the old certificate, that lists both DNs).
Required (while verifyUpdateCodeSignature is enabled and app-update.yml is written) whenever
no certificate supplies it: with a custom sign hook, even if a certificate is configured,
for a certificate without a CN, and when no certificate is configured (e.g. the build is signed
outside electron-builder) — otherwise the build fails. null is no opt-out since v27: a build that
writes app-update.yml with it fails; set verifyUpdateCodeSignature
to false instead.
See
https://github.com/electron-userland/electron-builder/issues/1187#issuecomment-278972073
Inherited from
WindowsSigningSharedOptions.publisherName
rfc3161TimeStampServer?
readonlyoptionalrfc3161TimeStampServer?:string|null
The URL of the RFC 3161 timestamp server, used for
SHA-256 and nested/appended signatures (signtool's /tr flag). Timestamping records when
the file was signed so the signature stays valid after the signing certificate expires.
Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.rfc3161TimeStampServer
sign?
readonlyoptionalsign?:string|CustomWindowsSign|null
A custom signing hook that replaces electron-builder's built-in signtool /
osslsigncode invocation. Provide a function, or the path / module id of a file that exports
a sign function (resolved relative to the project, then as a module).
The hook is invoked once per signing pass (i.e. once per entry in
signingHashAlgorithms) and receives a configuration object describing
the file to sign (path), the resolved certificate info (cscInfo), the current hash, and
whether this pass is a nested signature (isNest), plus a computeSignToolArgs(isWin) helper
that returns the default arguments electron-builder would otherwise have used. Use this to
integrate an external or cloud signing service. See
Code Signing.
electron-builder cannot tell which certificate the hook signs with, so also set
publisherName to that certificate's subject: it is never derived from a
certificate for a hook (not even one in the config), and without it a build that writes
app-update.yml fails.
Inherited from
WindowsSigningSharedOptions.sign
signingHashAlgorithms?
readonlyoptionalsigningHashAlgorithms?: ("sha256"|"sha1")[] |null
The digest (hash) algorithms to sign with, applied via signtool's /fd flag (or
osslsigncode's -h). One signing pass runs per entry, in order; listing more than one
dual-signs the file, with each additional signature appended as a nested signature
(signtool /as). Signing with both sha1 and sha256 lets a single binary validate on
legacy (pre-SHA-2) Windows as well as modern Windows.
Some targets override this: .msi cannot be dual-signed, so it is signed once, with sha256
unless sha1 is the only algorithm listed, and AppX/MSIX is always sha256 only.
Default
['sha1', 'sha256']
Inherited from
WindowsSigningSharedOptions.signingHashAlgorithms
timeStampServer?
readonlyoptionaltimeStampServer?:string|null
The URL of the legacy Authenticode timestamp server, used for SHA-1 signatures (signtool's
/t flag). This is also the timestamp server used by osslsigncode (-t) when signing on
macOS/Linux. See rfc3161TimeStampServer for SHA-256 / nested
signatures. Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.timeStampServer
type
readonlytype:"signtool"
Discriminator selecting the default file/store signing mode.
HSM Signing (type: "hsm") — Beta
HSM signing is available in v27 as a beta feature. The configuration interface is stable, but real-hardware test coverage is limited. Please report issues if you encounter problems.
Signs using a Hardware Security Module (HSM), FIPS-compliant token, or smart card via signtool.exe's /csp (cryptographic service provider) and /kc (key container) flags. The private key never leaves the hardware; only the public certificate chain needs to be accessible to electron-builder.
Platform: Windows only. For macOS/Linux CI without a Windows VM, use type: "pkcs11" instead.
Requirement: a modern winCodeSign toolset, which is the default — you do not need to pin a version. Only an explicit legacy pin (toolsets.winCodeSign: "0.0.0") is unsupported, because it predates the required signtool version.
Setup
- Install the CSP driver for your HSM/token on the build machine. Examples:
- Google Cloud KMS: the Cloud KMS Windows CNG Provider.
- Smart card / FIPS token:
"Microsoft Base Smart Card Crypto Provider"(built into Windows).
- Obtain the certificate chain file (
.crt,.cer, or.pfx— public key only). - Configure electron-builder:
{
"win": {
"sign": {
"type": "hsm",
"cryptoServiceProvider": "Google Cloud KMS Provider",
"keyContainer": "projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",
"certificateFile": "chain.crt"
}
}
}
The default winCodeSign toolset already supports HSM signing, so no toolsets pin is needed.
Execution flow
signtool.exe signis invoked with/csp <cryptoServiceProvider>and/kc <keyContainer>.- The certificate is identified via
certificateFile,certificateSha1, orcertificateSubjectName. - The HSM performs the private-key operation; the resulting signature is embedded by signtool.
- By default the file is dual-signed (SHA-1, then SHA-256), the same as the
signtoolmode — the HSM performs a key operation for each pass. SetsigningHashAlgorithms: ["sha256"]to produce a single SHA-256 signature instead.
Caveats
- Windows only. The
/cspand/kcflags are signtool-specific. On macOS/Linux, usetype: "pkcs11". - The CSP driver must be installed on the machine where the build runs (not available in most cloud CI environments without extra setup).
certificateFile,certificateSha1, orcertificateSubjectNamemust be provided unless a customsignhook does the signing — HSM signing requires a certificate identifier (unlike signtool where the cert can be embedded in the token implicitly).- Set
publisherNameto the certificate subject (a distinguished name) when electron-builder cannot read it — a customsignhook without a certificate identifier, or a.crt/.cerwithout a Common Name (CN). Otherwise a build that writesapp-update.ymlfails, because electron-updater needs the publisher name to verify updates.
Configuration reference
Interface: WindowsHsmSigningConfig
Beta
HSM signing is available in v27 as a beta feature. The interface is stable but real-hardware test coverage is limited.
Extends
WindowsSigningSharedOptions
Properties
additionalCertificateFile?
readonlyoptionaladditionalCertificateFile?:string|null
Beta
Path to an additional certificate file (typically an intermediate / cross-signing CA
certificate) whose contents are added to the signature block via signtool's /ac flag. Use
this when the signing certificate's full chain is not already present on target machines and
you want it embedded in the signature so the chain can be validated.
Inherited from
WindowsSigningSharedOptions.additionalCertificateFile
certificateFile?
readonlyoptionalcertificateFile?:string|null
Beta
Path to the certificate file containing the public certificate chain (.crt / .cer / .pfx).
The private key is NOT read from this file — it is provided by the HSM via cryptoServiceProvider.
certificateSha1?
readonlyoptionalcertificateSha1?:string|null
Beta
Alternative to certificateFile: locate the public certificate in the Windows certificate
store by its SHA-1 thumbprint. The private key still comes from the HSM via
cryptoServiceProvider / keyContainer; this only identifies which public certificate to sign
with.
certificateSubjectName?
readonlyoptionalcertificateSubjectName?:string|null
Beta
Alternative to certificateFile: locate the public certificate in the Windows certificate
store by (a substring of) its subject name. The private key still comes from the HSM via
cryptoServiceProvider / keyContainer; this only identifies which public certificate to sign
with.
cryptoServiceProvider
readonlycryptoServiceProvider:string
Beta
The name of the cryptographic service provider (CSP) that holds the private key.
Maps to signtool's /csp flag. Examples:
- Google Cloud KMS:
"Google Cloud KMS Provider" - Smart card / FIPS token:
"Microsoft Base Smart Card Crypto Provider"
Requires a modern winCodeSign toolset (the default; only the legacy "0.0.0" pin is
unsupported). Windows-only — use type: "pkcs11" on macOS/Linux.
keyContainer
readonlykeyContainer:string
Beta
The key container name within the CSP. Maps to signtool's /kc flag.
Example for Google Cloud KMS:
"projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/1"
publisherName?
readonlyoptionalpublisherName?:string|string[] |null
Beta
The publisher name(s) to associate with the signature: a Distinguished Name (DN) of your code
signing certificate's subject, written with the attribute names Windows uses (e.g.
CN=My Company, O=My Company, C=US). Every attribute you list must equal the certificate's;
attributes you leave out are not compared. A bare name (My Company) fails the build since v27,
because electron-updater 7 never matches it — see
the migration guide.
May be a single string or an array — an array is useful when rotating certificates, so updates
signed by either the old or the new certificate still verify.
This value is not a signtool argument; it is consumed in two places:
- Update verification — when verifyUpdateCodeSignature is
enabled, it is written into
app-update.ymland electron-updater checks it against the certificate that signed each downloaded update. - AppX / MSIX identity — the package
Publisherattribute must equal the certificate subject, so electron-builder derives it from this value (or the certificate) to keep them in sync; a mismatch makes packaging fail withERROR_BAD_FORMAT.
Defaults to CN=<common name> of your code signing certificate, so a renewed certificate with the
same common name keeps updates working even when its other subject attributes change. For a stricter
pin, set a fuller DN explicitly (a renewal that changes a configured attribute then needs a release,
signed with the old certificate, that lists both DNs).
Required (while verifyUpdateCodeSignature is enabled and app-update.yml is written) whenever
no certificate supplies it: with a custom sign hook, even if a certificate is configured,
for a certificate without a CN, and when no certificate is configured (e.g. the build is signed
outside electron-builder) — otherwise the build fails. null is no opt-out since v27: a build that
writes app-update.yml with it fails; set verifyUpdateCodeSignature
to false instead.
See
https://github.com/electron-userland/electron-builder/issues/1187#issuecomment-278972073
Inherited from
WindowsSigningSharedOptions.publisherName
rfc3161TimeStampServer?
readonlyoptionalrfc3161TimeStampServer?:string|null
Beta
The URL of the RFC 3161 timestamp server, used for
SHA-256 and nested/appended signatures (signtool's /tr flag). Timestamping records when
the file was signed so the signature stays valid after the signing certificate expires.
Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.rfc3161TimeStampServer
sign?
readonlyoptionalsign?:string|CustomWindowsSign|null
Beta
A custom signing hook that replaces electron-builder's built-in signtool /
osslsigncode invocation. Provide a function, or the path / module id of a file that exports
a sign function (resolved relative to the project, then as a module).
The hook is invoked once per signing pass (i.e. once per entry in
signingHashAlgorithms) and receives a configuration object describing
the file to sign (path), the resolved certificate info (cscInfo), the current hash, and
whether this pass is a nested signature (isNest), plus a computeSignToolArgs(isWin) helper
that returns the default arguments electron-builder would otherwise have used. Use this to
integrate an external or cloud signing service. See
Code Signing.
electron-builder cannot tell which certificate the hook signs with, so also set
publisherName to that certificate's subject: it is never derived from a
certificate for a hook (not even one in the config), and without it a build that writes
app-update.yml fails.
Inherited from
WindowsSigningSharedOptions.sign
signingHashAlgorithms?
readonlyoptionalsigningHashAlgorithms?: ("sha256"|"sha1")[] |null
Beta
The digest (hash) algorithms to sign with, applied via signtool's /fd flag (or
osslsigncode's -h). One signing pass runs per entry, in order; listing more than one
dual-signs the file, with each additional signature appended as a nested signature
(signtool /as). Signing with both sha1 and sha256 lets a single binary validate on
legacy (pre-SHA-2) Windows as well as modern Windows.
Some targets override this: .msi cannot be dual-signed, so it is signed once, with sha256
unless sha1 is the only algorithm listed, and AppX/MSIX is always sha256 only.
Default
['sha1', 'sha256']
Inherited from
WindowsSigningSharedOptions.signingHashAlgorithms
timeStampServer?
readonlyoptionaltimeStampServer?:string|null
Beta
The URL of the legacy Authenticode timestamp server, used for SHA-1 signatures (signtool's
/t flag). This is also the timestamp server used by osslsigncode (-t) when signing on
macOS/Linux. See rfc3161TimeStampServer for SHA-256 / nested
signatures. Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.timeStampServer
type
readonlytype:"hsm"
Beta
Discriminator selecting HSM / hardware-token signing via signtool's /csp and /kc flags.
PKCS#11 Signing (type: "pkcs11") — Beta
PKCS#11 signing is available in v27 as a beta feature. The configuration interface is stable, but real-hardware test coverage is limited. Please report issues if you encounter problems.
Signs using a PKCS#11 hardware token via osslsigncode. Unlike HSM signing, this works on macOS and Linux without a Windows VM, making it suitable for cloud CI environments where a physical token is accessible via network HSM or PKCS#11-over-network software.
Platform: macOS and Linux only (uses osslsigncode). On Windows, use type: "hsm" instead.
Setup
- Install the PKCS#11 shared library for your token. Examples:
- OpenSC (smart cards):
sudo apt install openscorbrew install opensc - SoftHSM (testing):
sudo apt install softhsm2 - AWS CloudHSM: the CloudHSM PKCS#11 library
- OpenSC (smart cards):
- Confirm the module path and key URI using
pkcs11-tool --list-objectsor your provider's tooling. - Configure electron-builder:
{
"win": {
"sign": {
"type": "pkcs11",
"pkcs11Module": "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so",
"pkcs11KeyUri": "pkcs11:token=MyToken;object=MyKey;type=private",
"publisherName": "CN=My Company, O=My Company, C=US"
}
}
}
With an optional external certificate chain file (if the token does not carry the full chain):
{
"win": {
"sign": {
"type": "pkcs11",
"pkcs11Module": "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so",
"pkcs11KeyUri": "pkcs11:token=MyToken;object=MyKey;type=private",
"certificateFile": "chain.crt"
}
}
}
Token PIN
| Scenario | How to supply the PIN |
|---|---|
certificateFile set | WIN_CSC_KEY_PASSWORD or CSC_KEY_PASSWORD env var |
No certificateFile (token holds cert) | Same env vars, or embed directly in the URI: pkcs11:...?pin-value=<pin> |
Execution flow
osslsigncode signis invoked with-pkcs11moduleand-key(the RFC 7512 URI).- If
certificateFileis set,-certsis added for the chain; otherwise the cert embedded in the token is used. - If a PIN is available via env var,
-passis appended. - SHA1 and SHA256 signing are performed in sequence (like signtool dual-sign).
Caveats
- macOS/Linux only. Throws
InvalidConfigurationErrorwhen called on Windows; usetype: "hsm"on Windows. - The
osslsigncodebinary is bundled in thewinCodeSigntoolset — no manual installation needed. - The PKCS#11 shared library (
.so/.dylib) must be installed separately on the build machine. publisherName(the certificate subject) is required when there is nocertificateFile, and, for builds that writeapp-update.yml, when thecertificateFilehas no Common Name (CN) — electron-builder cannot read the certificate on the token.- Network or USB token latency can make large builds slow. Consider signing only final installers rather than every intermediate
.exe.
Configuration reference
Interface: WindowsPkcs11SigningConfig
Beta
Sign with a PKCS#11 hardware token (smart card, USB HSM, cloud KMS exposed via a PKCS#11
provider) using osslsigncode. Unlike hsm, this mode does not require Windows or a
Windows VM, so it can sign Windows binaries directly from macOS/Linux CI. The token PIN is read
from the WIN_CSC_KEY_PASSWORD (or CSC_KEY_PASSWORD) environment variable.
PKCS#11 signing is available in v27 as a beta feature. The interface is stable but real-hardware test coverage is limited.
Extends
WindowsSigningSharedOptions
Properties
additionalCertificateFile?
readonlyoptionaladditionalCertificateFile?:string|null
Beta
Path to an additional certificate file (typically an intermediate / cross-signing CA
certificate) whose contents are added to the signature block via signtool's /ac flag. Use
this when the signing certificate's full chain is not already present on target machines and
you want it embedded in the signature so the chain can be validated.
Inherited from
WindowsSigningSharedOptions.additionalCertificateFile
certificateFile?
readonlyoptionalcertificateFile?:string|null
Beta
Optional path to a certificate chain file to accompany the PKCS#11 key. If omitted the certificate embedded in the token is used.
pkcs11KeyUri
readonlypkcs11KeyUri:string
Beta
RFC 7512 PKCS#11 URI identifying the private key within the module.
Maps to osslsigncode's -key parameter.
Example: "pkcs11:token=MyToken;object=MyKey;type=private"
Must be paired with pkcs11Module.
pkcs11Module
readonlypkcs11Module:string
Beta
Path to the PKCS#11 shared library (.so on Linux, .dylib on macOS).
The library must be installed separately — electron-builder does not bundle it.
Example: "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so"
Must be paired with pkcs11KeyUri.
publisherName?
readonlyoptionalpublisherName?:string|string[] |null
Beta
The publisher name(s) to associate with the signature: a Distinguished Name (DN) of your code
signing certificate's subject, written with the attribute names Windows uses (e.g.
CN=My Company, O=My Company, C=US). Every attribute you list must equal the certificate's;
attributes you leave out are not compared. A bare name (My Company) fails the build since v27,
because electron-updater 7 never matches it — see
the migration guide.
May be a single string or an array — an array is useful when rotating certificates, so updates
signed by either the old or the new certificate still verify.
This value is not a signtool argument; it is consumed in two places:
- Update verification — when verifyUpdateCodeSignature is
enabled, it is written into
app-update.ymland electron-updater checks it against the certificate that signed each downloaded update. - AppX / MSIX identity — the package
Publisherattribute must equal the certificate subject, so electron-builder derives it from this value (or the certificate) to keep them in sync; a mismatch makes packaging fail withERROR_BAD_FORMAT.
Defaults to CN=<common name> of your code signing certificate, so a renewed certificate with the
same common name keeps updates working even when its other subject attributes change. For a stricter
pin, set a fuller DN explicitly (a renewal that changes a configured attribute then needs a release,
signed with the old certificate, that lists both DNs).
Required (while verifyUpdateCodeSignature is enabled and app-update.yml is written) whenever
no certificate supplies it: with a custom sign hook, even if a certificate is configured,
for a certificate without a CN, and when no certificate is configured (e.g. the build is signed
outside electron-builder) — otherwise the build fails. null is no opt-out since v27: a build that
writes app-update.yml with it fails; set verifyUpdateCodeSignature
to false instead.
See
https://github.com/electron-userland/electron-builder/issues/1187#issuecomment-278972073
Inherited from
WindowsSigningSharedOptions.publisherName
rfc3161TimeStampServer?
readonlyoptionalrfc3161TimeStampServer?:string|null
Beta
The URL of the RFC 3161 timestamp server, used for
SHA-256 and nested/appended signatures (signtool's /tr flag). Timestamping records when
the file was signed so the signature stays valid after the signing certificate expires.
Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.rfc3161TimeStampServer
sign?
readonlyoptionalsign?:string|CustomWindowsSign|null
Beta
A custom signing hook that replaces electron-builder's built-in signtool /
osslsigncode invocation. Provide a function, or the path / module id of a file that exports
a sign function (resolved relative to the project, then as a module).
The hook is invoked once per signing pass (i.e. once per entry in
signingHashAlgorithms) and receives a configuration object describing
the file to sign (path), the resolved certificate info (cscInfo), the current hash, and
whether this pass is a nested signature (isNest), plus a computeSignToolArgs(isWin) helper
that returns the default arguments electron-builder would otherwise have used. Use this to
integrate an external or cloud signing service. See
Code Signing.
electron-builder cannot tell which certificate the hook signs with, so also set
publisherName to that certificate's subject: it is never derived from a
certificate for a hook (not even one in the config), and without it a build that writes
app-update.yml fails.
Inherited from
WindowsSigningSharedOptions.sign
signingHashAlgorithms?
readonlyoptionalsigningHashAlgorithms?: ("sha256"|"sha1")[] |null
Beta
The digest (hash) algorithms to sign with, applied via signtool's /fd flag (or
osslsigncode's -h). One signing pass runs per entry, in order; listing more than one
dual-signs the file, with each additional signature appended as a nested signature
(signtool /as). Signing with both sha1 and sha256 lets a single binary validate on
legacy (pre-SHA-2) Windows as well as modern Windows.
Some targets override this: .msi cannot be dual-signed, so it is signed once, with sha256
unless sha1 is the only algorithm listed, and AppX/MSIX is always sha256 only.
Default
['sha1', 'sha256']
Inherited from
WindowsSigningSharedOptions.signingHashAlgorithms
timeStampServer?
readonlyoptionaltimeStampServer?:string|null
Beta
The URL of the legacy Authenticode timestamp server, used for SHA-1 signatures (signtool's
/t flag). This is also the timestamp server used by osslsigncode (-t) when signing on
macOS/Linux. See rfc3161TimeStampServer for SHA-256 / nested
signatures. Ignored when the build runs with ELECTRON_BUILDER_OFFLINE=true.
Default
http://timestamp.digicert.com
Inherited from
WindowsSigningSharedOptions.timeStampServer
type
readonlytype:"pkcs11"
Beta
Discriminator selecting cross-platform PKCS#11 token signing via osslsigncode.
Azure Trusted Signing (type: "azure") — Beta
The signtool /dlib Azure Trusted Signing integration introduced in v27 is a beta feature. It is the default path on current toolsets; the legacy PowerShell fallback remains available only when you explicitly pin toolsets.winCodeSign below "1.3.0" (see Legacy PowerShell fallback below). Please report issues if you encounter problems.
Microsoft's cloud-based code signing service. No certificate file or private key is managed locally — authentication is via Azure Entra ID environment variables and signing is performed by the Azure service.
Azure account setup
If you do not already have an Azure setup and only want to use their code signing service, set up an Azure "Trusted Signing Account" using this quickstart guide. Then, set up an "App registration" in Azure, follow the steps to create a "Secret" for it, and assign the role "Trusted Signing Certificate Profile Signer" to the App registration (note, the App registration is considered a "service principal" and you will need to type its name into the search bar to find it in the web panel).
Setup
Set the required environment variables (descriptions from Azure.Identity — EnvironmentCredential):
| Env var | Description |
|---|---|
AZURE_TENANT_ID | Your Azure AD Tenant ID; found in the Entra ID portal |
AZURE_CLIENT_ID | Application (Client) ID of your App registration — not the object ID |
AZURE_CLIENT_SECRET | Value of the Secret you created — not the secret's ID |
AZURE_CLIENT_CERTIFICATE_PATH | Required if using your own certificate for authentication |
AZURE_CLIENT_SEND_CERTIFICATE_CHAIN | Required if using your own certificate for authentication |
AZURE_USERNAME | Microsoft Entra account username (for interactive/password flows) |
AZURE_PASSWORD | Microsoft Entra account password (for interactive/password flows) |
AZURE_FEDERATED_TOKEN_FILE | For workload identity federation (e.g. GitHub Actions OIDC) |
If you use the minimal setup with an "App registration" as described above, only AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET are needed.
Then configure win.sign. The default toolset already ships the /dlib integration, so no winCodeSign pin is required:
{
"win": {
"sign": {
"type": "azure",
"publisherName": "CN=My Company, O=My Company, C=US",
"endpoint": "https://weu.codesigning.azure.net/",
"codeSigningAccountName": "my-trusted-signing-account",
"certificateProfileName": "my-cert-profile"
}
}
}
Execution flow
- electron-builder writes a
metadata.jsonfile containingEndpoint,CodeSigningAccountName,CertificateProfileName, and anyadditionalMetadatafields. signtool.exe sign /dlib Azure.CodeSigning.Dlib.dll /dmdf metadata.jsonis invoked. The DLib authenticates to Azure usingDefaultAzureCredential(reads the env vars above automatically).- On macOS/Linux,
signtool.exeruns inside Wine using thewinCodeSigntoolset bundle — no separate Windows VM required.
Extra metadata fields
Use additionalMetadata to pass extra fields verbatim into metadata.json. This covers DLib-specific options not exposed as typed fields:
{
"win": {
"sign": {
"type": "azure",
"endpoint": "https://weu.codesigning.azure.net/",
"codeSigningAccountName": "my-account",
"certificateProfileName": "my-profile",
"publisherName": "CN=My Company",
"additionalMetadata": {
"ExcludeCredentials": "ManagedIdentityCredential",
"CorrelationId": "my-build-run-id"
}
}
}
}
Legacy PowerShell fallback
The default toolset (winCodeSign unset, null, or "latest") resolves to the newest bundle (1.3.0+) and uses the modern signtool /dlib path. Only if you explicitly pin toolsets.winCodeSign below "1.3.0" (for example "1.2.1", or the legacy "0.0.0") does electron-builder fall back to the v26 PowerShell Invoke-TrustedSigning integration and emit a deprecation warning. This fallback will be removed in a future release, so avoid pinning an older toolset unless you specifically need it.
Caveats
- The
ats-bundle(containingAzure.CodeSigning.Dlib.dlland its native dependency closure) and thedotnet-runtimebundle ship withwinCodeSign "1.3.0"and later — which is the default. They are downloaded automatically on first use; no manual setup or toolset pin is required. - The DLib is a mixed-mode .NET 8 assembly: electron-builder automatically downloads and provides the .NET 8 runtime via the separate
dotnet-runtimebundle — no manual runtime installation is required. - The DLib authenticates using the Azure environment variables at signing time — credentials are not embedded in config.
- Network connectivity to the Azure endpoint is required during the build. Rate limits or outages will fail the build.
Configuration reference
Interface: WindowsAzureSigningConfig
The signtool /dlib Azure Trusted Signing integration was introduced in toolsets.winCodeSign: 1.3.0.
The legacy PowerShell integration is deprecated, but leverages the same underlying Azure APIs, so both interfaces share the same config shape.
Properties
additionalMetadata?
readonlyoptionaladditionalMetadata?:Record<string,string>
Additional fields to include verbatim in the metadata.json file passed to
Azure.CodeSigning.Dlib.dll via signtool /dmdf. Use this for DLib-specific options
not covered by the typed fields above (e.g. ExcludeCredentials, CorrelationId).
certificateProfileName
readonlycertificateProfileName:string
The name of the Trusted Signing Certificate Profile to sign with, as created in your Azure
Code Signing Account. Maps to the DLib metadata field CertificateProfileName.
codeSigningAccountName
readonlycodeSigningAccountName:string
The name of the Azure Trusted Signing (Code Signing) Account that owns the certificate profile.
Maps to the DLib metadata field CodeSigningAccountName.
endpoint
readonlyendpoint:string
The Trusted Signing Account endpoint. The URI value must align to the region your Trusted Signing Account and Certificate Profile were created in.
Requires Azure Entra ID environment variables per Microsoft's documentation.
fileDigest?
readonlyoptionalfileDigest?:string
The digest algorithm used to hash the files being signed. Maps to the DLib metadata field
FileDigest.
Default
SHA256
publisherName
readonlypublisherName:string
The publisher name to associate with the signature: a Distinguished Name (DN) of the subject of
the certificate issued by your Trusted Signing certificate profile (e.g.
CN=My Company, O=My Company, C=US). Required. Used for
update verification (embedded in app-update.yml and checked by
electron-updater) and must match the certificate subject. A bare name fails the build since
v27 — see
the migration guide.
See
https://github.com/electron-userland/electron-builder/issues/1187#issuecomment-278972073
timestampDigest?
readonlyoptionaltimestampDigest?:string
The timestamp digest algorithm. Translates to field: TimestampDigest.
Default
SHA256
timestampRfc3161?
readonlyoptionaltimestampRfc3161?:string
The RFC3161 timestamp server. Translates to field: TimestampRfc3161.
Default
http://timestamp.acs.microsoft.com
type
readonlytype:"azure"
Discriminator selecting Azure Trusted Signing (cloud signing — no local certificate).