Skip to content

Commit f938c52

Browse files
committed
docs: document Azure Trusted Signing workflow
1 parent 2f23fda commit f938c52

File tree

1 file changed

+135
-4
lines changed

1 file changed

+135
-4
lines changed

guides/code-signing/code-signing-windows.md

+135-4
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@ description: >-
66

77
# Signing a Windows app
88

9+
## Using traditional certificates
10+
911
{% hint style="warning" %}
1012
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.\
1113
\
1214
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.
1315
{% endhint %}
1416

15-
## Prerequisites
17+
### Prerequisites
1618

17-
### Installing Visual Studio
19+
#### Installing Visual Studio
1820

1921
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).
2022

21-
### Acquiring a certificate
23+
#### Acquiring a certificate
2224

2325
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:
2426

@@ -32,7 +34,7 @@ You can get a [Windows Authenticode](https://learn.microsoft.com/en-us/windows-h
3234
Your certificate password should be a **secret**. Do not share it publicly or commit it to your source code.
3335
{% endhint %}
3436

35-
## Configuring Electron Forge
37+
### Configuring Electron Forge
3638

3739
On Windows, Electron apps are signed on the installer level at the **Make** step.
3840

@@ -56,3 +58,132 @@ module.exports = {
5658
};
5759
```
5860
{% endcode %}
61+
62+
## Using Azure Trusted Signing
63+
64+
[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.
65+
66+
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.
67+
68+
### Prerequisites
69+
70+
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/).
71+
72+
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.
73+
74+
### Configuring Electron Forge
75+
76+
#### Installing npm dependencies
77+
78+
In your project directory, do the following:
79+
80+
1. Install the `dotenv-cli` package: `npm i -D dotenv-cli`
81+
2. Update `@electron/windows-sign` to version 1.2.0 or later: `npm update @electron/windows-sign`
82+
83+
#### Creating the `.env.trustedsigning` file
84+
85+
Create a file `.env.trustedsigning` in your project root with the following content:
86+
87+
{% code title=".env.trustedsigning" %}
88+
```text
89+
AZURE_CLIENT_ID='xxx'
90+
AZURE_CLIENT_SECRET='xxx'
91+
AZURE_TENANT_ID='xxx'
92+
AZURE_METADATA_JSON='C:\path\to\metadata.json'
93+
AZURE_CODE_SIGNING_DLIB='C:\path\to\bin\x64\Azure.CodeSigning.Dlib.dll'
94+
SIGNTOOL_PATH='C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe'
95+
```
96+
{% endcode %}
97+
98+
Fill in the credentials for your Azure App Registration user into the first three variables.
99+
100+
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.
101+
102+
{% hint style="warning" %}
103+
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.)
104+
{% endhint %}
105+
106+
#### Adjusting your `.gitignore`
107+
108+
Add `.env.trustedsigning` to your `.gitignore` file. You should never commit login credentials to version control.
109+
110+
In addition, add `electron-windows-sign.log` to `.gitignore`. This file will be created automatically during the signing process.
111+
112+
{% code title=".gitignore" %}
113+
```gitignore
114+
.env.trustedsigning
115+
electron-windows-sign.log
116+
```
117+
{% endcode %}
118+
119+
#### Creating the `windowsSign.ts` file
120+
121+
Create a file `windowsSign.ts` in your project root with the following content:
122+
123+
{% code title="windowsSign.ts" %}
124+
```typescript
125+
import type { WindowsSignOptions } from "@electron/packager";
126+
import type { HASHES } from "@electron/windows-sign/dist/esm/types";
127+
128+
export const windowsSign: WindowsSignOptions = {
129+
...(process.env.SIGNTOOL_PATH
130+
? { signToolPath: process.env.SIGNTOOL_PATH }
131+
: {}),
132+
signWithParams: `/v /debug /dlib ${process.env.AZURE_CODE_SIGNING_DLIB} /dmdf ${process.env.AZURE_METADATA_JSON}`,
133+
timestampServer: "http://timestamp.acs.microsoft.com",
134+
hashes: ["sha256" as HASHES],
135+
};
136+
```
137+
{% endcode %}
138+
139+
{% hint style="info" %}
140+
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.
141+
{% endhint %}
142+
143+
Some notes:
144+
145+
We specify the `/v` and `/debug` parameters even though they aren't technically required. This ensures that warnings are logged if timestamping fails.
146+
147+
**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.)
148+
149+
#### Adjusting your `forge.config.ts`
150+
151+
In your `forge.config.ts`, add the following:
152+
153+
{% code title="forge.config.ts" %}
154+
```typescript
155+
// Add import:
156+
import { windowsSign } from "./src/util/windowsSign";
157+
158+
const config: ForgeConfig = {
159+
packagerConfig: {
160+
// Add this line:
161+
windowsSign,
162+
},
163+
makers: [
164+
new MakerSquirrel({
165+
// Add the following two lines:
166+
// @ts-expect-error - incorrect types exported by MakerSquirrel
167+
windowsSign,
168+
}),
169+
],
170+
};
171+
```
172+
{% endcode %}
173+
174+
#### Updating your npm scripts
175+
176+
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.
177+
178+
For example, your npm scripts in your `package.json` might then look like this:
179+
180+
{% code title="package.json" %}
181+
```json
182+
{
183+
"scripts": {
184+
"make": "dotenv -e .env.trustedsigning -- electron-forge make",
185+
"publish": "dotenv -e .env.trustedsigning -- electron-forge publish"
186+
}
187+
}
188+
```
189+
{% endcode %}

0 commit comments

Comments
 (0)