Signing a Windows app
Code signing is a security technology that you use to certify that an app was created by you.
Using traditional certificates
Starting June 1, 2023 at 00:00 UTC, private keys for code signing certificates need to be stored on a hardware storage module compliant with FIPS 140 Level 2, Common Criteria EAL 4+ or equivalent. In practice, this means that software-based OV certificates used in the steps below will no longer be available for purchase. For instructions on how to sign applications with newer token-based certificates, consult your Certificate Authority's documentation.
Prerequisites
Installing Visual Studio
On Windows, apps are signed using Sign Tool, which is included in Visual Studio. Install Visual Studio to get the signing utility (the free Community Edition is enough).
Acquiring a certificate
You can get a Windows Authenticode code signing certificate from many vendors. Prices vary, so it may be worth your time to shop around. Popular vendors include:
Amongst others, please shop around to find one that suits your needs! 😄
Keep your certificate password private
Your certificate password should be a secret. Do not share it publicly or commit it to your source code.
Configuring Electron Forge
On Windows, Electron apps are signed on the installer level at the Make step.
Once you have a Personal Information Exchange (.pfx
) file for your certificate, you can sign Squirrel.Windows and MSI installers in Electron Forge with the certificateFile
and certificatePassword
fields in their respective configuration objects.
For example, if you are creating a Squirrel.Windows installer:
module.exports = {
packagerConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
certificateFile: './cert.pfx',
certificatePassword: process.env.CERTIFICATE_PASSWORD
}
}
]
};
Using Azure Trusted Signing
Azure Trusted Signing is Microsoft's modern cloud-based alternative to EV certificates. It is the cheapest option for code signing on Windows, and it gets rid of SmartScreen warnings.
As of May 2025, Azure Trusted Signing is available to US and Canada-based organizations with 3+ years of verifiable business history. Microsoft is looking to make the program more widely available. If you're reading this at a later point, it could make sense to check if the eligibility criteria have changed.
Prerequisites
First, create an Azure account and set up Azure Trusted Signing in your account as described here.
Then install the dependencies for local code signing as described here. Also create the required metadata.json
file in an arbitrary location on your computer.
Configuring Electron Forge
Installing npm dependencies
In your project directory, do the following:
Install the
dotenv-cli
package:npm i -D dotenv-cli
Update
@electron/windows-sign
to version 1.2.2 or later:npm update @electron/windows-sign
Creating the .env.trustedsigning
file
.env.trustedsigning
fileCreate a file .env.trustedsigning
in your project root with the following content:
AZURE_CLIENT_ID='xxx'
AZURE_CLIENT_SECRET='xxx'
AZURE_TENANT_ID='xxx'
AZURE_METADATA_JSON='C:\path\to\metadata.json'
AZURE_CODE_SIGNING_DLIB='C:\path\to\bin\x64\Azure.CodeSigning.Dlib.dll'
SIGNTOOL_PATH='C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe'
Fill in the credentials for your Azure App Registration user into the first three variables.
Adjust the other variables to be the absolute paths to the metadata.json
, Azure.CodeSigning.Dlib.dll
and signtool.exe
files that you created or installed as part of the prerequisites.
Ensure that none of the paths have spaces in them. Otherwise, signing will fail. (@electron/windows-sign
issue #45 currently prevents quoting of paths with spaces.)
Adjusting your .gitignore
.gitignore
Add .env.trustedsigning
to your .gitignore
file. You should never commit login credentials to version control.
In addition, add electron-windows-sign.log
to .gitignore
. This file will be created automatically during the signing process.
.env.trustedsigning
electron-windows-sign.log
Creating the windowsSign.ts
file
windowsSign.ts
fileCreate a file windowsSign.ts
in your project root with the following content:
import type { WindowsSignOptions } from "@electron/packager";
import type { HASHES } from "@electron/windows-sign/dist/esm/types";
export const windowsSign: WindowsSignOptions = {
...(process.env.SIGNTOOL_PATH
? { signToolPath: process.env.SIGNTOOL_PATH }
: {}),
signWithParams: `/v /debug /dlib ${process.env.AZURE_CODE_SIGNING_DLIB} /dmdf ${process.env.AZURE_METADATA_JSON}`,
timestampServer: "http://timestamp.acs.microsoft.com",
hashes: ["sha256" as HASHES],
};
Some notes:
We specify the /v
and /debug
parameters even though they aren't technically required. This ensures that warnings are logged if timestamping fails.
Adjusting your forge.config.ts
forge.config.ts
In your forge.config.ts
, add the following:
// Add import:
import { windowsSign } from "./windowsSign";
const config: ForgeConfig = {
packagerConfig: {
// Add this line:
windowsSign,
},
makers: [
new MakerSquirrel({
// Add the following two lines:
// @ts-expect-error - incorrect types exported by MakerSquirrel
windowsSign,
}),
],
};
Updating your npm scripts
When you call scripts such as electron-forge make
or electron-forge publish
, you will now have to prefix them with dotenv -e .env.trustedsigning --
. This loads the environment variables from the .env.trustedsigning
file.
For example, your npm scripts in your package.json
might then look like this:
{
"scripts": {
"make": "dotenv -e .env.trustedsigning -- electron-forge make",
"publish": "dotenv -e .env.trustedsigning -- electron-forge publish"
}
}
Last updated
Was this helpful?