diff --git a/guides/code-signing/code-signing-windows.md b/guides/code-signing/code-signing-windows.md index d58afaa..3154985 100644 --- a/guides/code-signing/code-signing-windows.md +++ b/guides/code-signing/code-signing-windows.md @@ -6,19 +6,21 @@ description: >- # Signing a Windows app +## Using traditional certificates + {% hint style="warning" %} 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. {% endhint %} -## Prerequisites +### Prerequisites -### Installing Visual Studio +#### Installing Visual Studio On Windows, apps are signed using [Sign Tool](https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe), which is included in Visual Studio. Install Visual Studio to get the signing utility (the free [Community Edition](https://visualstudio.microsoft.com/vs/community/) is enough). -### Acquiring a certificate +#### Acquiring a certificate You can get a [Windows Authenticode](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/authenticode) code signing certificate from many vendors. Prices vary, so it may be worth your time to shop around. Popular vendors include: @@ -32,7 +34,7 @@ You can get a [Windows Authenticode](https://learn.microsoft.com/en-us/windows-h Your certificate password should be a **secret**. Do not share it publicly or commit it to your source code. {% endhint %} -## Configuring Electron Forge +### Configuring Electron Forge On Windows, Electron apps are signed on the installer level at the **Make** step. @@ -56,3 +58,133 @@ module.exports = { }; ``` {% endcode %} + +## Using Azure Trusted Signing + +[Azure Trusted Signing](https://azure.microsoft.com/en-us/products/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 November 2024, Azure Trusted Signing is available to organizations with 3+ years of verifiable business history and to individuals. Microsoft is [looking to make the program available](https://github.com/Azure/trusted-signing-action/issues/42#issuecomment-2488402061) to organizations with a shorter history, too. If you're reading this at a later point, it could make sense to check. + +### Prerequisites + +First, create an Azure account and set up Azure Trusted Signing in your account as described [here](https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/). + +Then install the dependencies for local code signing as described [here](https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/#step-8-signing-locally). 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: + +1. Install the `dotenv-cli` package: `npm i -D dotenv-cli` +2. Update `@electron/windows-sign` to version 1.2.0 or later: `npm update @electron/windows-sign` + +#### Creating the `.env.trustedsigning` file + +Create a file `.env.trustedsigning` in your project root with the following content: + +{% code title=".env.trustedsigning" %} +```text +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' +``` +{% endcode %} + +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. + +{% hint style="warning" %} +Ensure that none of the paths have spaces in them. Otherwise, signing will fail. (`@electron/windows-sign` issue [#45](https://github.com/electron/windows-sign/issues/45) currently prevents quoting of paths with spaces.) +{% endhint %} + +#### Adjusting your `.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. + +{% code title=".gitignore" %} +```gitignore +.env.trustedsigning +electron-windows-sign.log +``` +{% endcode %} + +#### Creating the `windowsSign.ts` file + +Create a file `windowsSign.ts` in your project root with the following content: + +{% code title="windowsSign.ts" %} +```typescript +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], +}; +``` +{% endcode %} + +{% hint style="info" %} +If you are using JavaScript for your configuration instead of TypeScript, adjust the file accordingly. Name the file `windowsSign.js` and remove the type information. +{% endhint %} + +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. + +**Do not** use the `debug` parameter on the `WindowsSignOptions`. Similarly, **do not** enable the `DEBUG` environment variable for `electron-windows-sign`. (If you do either of them, the `debug` npm package will log all debug messages to stderr. An executable in `@electron/windows-sign` will interpret the existence of messages printed to stderr as a signing failure. Then your build will fail.) + +#### Adjusting your `forge.config.ts` + +In your `forge.config.ts`, add the following: + +{% code title="forge.config.ts" %} +```typescript +// Add import: +import { windowsSign } from "./src/util/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, + }), + ], +}; +``` +{% endcode %} + +#### 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: + +{% code title="package.json" %} +```json +{ + "scripts": { + "make": "dotenv -e .env.trustedsigning -- electron-forge make", + "publish": "dotenv -e .env.trustedsigning -- electron-forge publish" + } +} +``` +{% endcode %}