Skip to content

Commit e1d2285

Browse files
committed
Add Azure Trusted Signing to build workflow
1 parent a50b806 commit e1d2285

2 files changed

Lines changed: 177 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,27 @@ jobs:
7979
/p:FileVersion=$env:ASSEMBLY_VERSION `
8080
/p:AssemblyInformationalVersion=$env:FULLVERSION `
8181
--self-contained
82+
83+
- name: Azure Login
84+
uses: azure/login@v2
85+
with:
86+
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
87+
88+
- name: Sign executables with Trusted Signing
89+
uses: azure/trusted-signing-action@v0
90+
with:
91+
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
92+
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
93+
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
94+
endpoint: https://wus2.codesigning.azure.net/
95+
trusted-signing-account-name: hanselman
96+
certificate-profile-name: WindowsEdgeLight
97+
files-folder: ${{ github.workspace }}\WindowsEdgeLight\bin\Release\net10.0-windows
98+
files-folder-filter: exe
99+
files-folder-recurse: true
100+
file-digest: SHA256
101+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
102+
timestamp-digest: SHA256
82103

83104
- name: Prepare artifacts
84105
shell: pwsh

CODESIGNING.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Code Signing with Azure Trusted Signing
2+
3+
This document explains how WindowsEdgeLight is signed using Azure Trusted Signing to prevent Microsoft Defender SmartScreen warnings.
4+
5+
## Overview
6+
7+
WindowsEdgeLight uses Azure Trusted Signing to digitally sign executables, which:
8+
- Eliminates SmartScreen warnings for users
9+
- Verifies the authenticity of the application
10+
- Establishes trust with Windows security features
11+
12+
## Azure Setup
13+
14+
### Resources
15+
- **Subscription ID**: `<your-subscription-id>`
16+
- **Resource Group**: `<your-resource-group>`
17+
- **Code Signing Account**: `<your-account-name>`
18+
- **Certificate Profile**: `<your-certificate-profile-name>`
19+
- **Endpoint**: `https://wus2.codesigning.azure.net/` (West US 2)
20+
- **Region**: West US 2
21+
22+
### Service Principal for GitHub Actions
23+
A service principal is configured with the "Trusted Signing Certificate Profile Signer" role on the Code Signing Account.
24+
25+
## Local Signing
26+
27+
### Prerequisites
28+
1. Azure CLI installed and logged in with the correct scope:
29+
```powershell
30+
az logout
31+
az login --use-device-code --scope "https://codesigning.azure.net/.default"
32+
```
33+
34+
2. User account needs "Trusted Signing Certificate Profile Signer" role assigned on the Code Signing Account
35+
36+
3. Download `sign.exe` from [dotnet/sign releases](https://github.com/dotnet/sign/releases)
37+
38+
### Sign Executables Locally
39+
```powershell
40+
# Place executables in publish folder
41+
.\sign.exe code trusted-signing `
42+
-b <path-to-publish-folder> `
43+
-tse "https://wus2.codesigning.azure.net" `
44+
-tscp <certificate-profile-name> `
45+
-tsa <code-signing-account-name> `
46+
*.exe `
47+
-v Trace
48+
```
49+
50+
### Verify Signature
51+
```powershell
52+
Get-AuthenticodeSignature .\publish\WindowsEdgeLight-v1.0.0-win-x64.exe
53+
54+
# Should show:
55+
# SignerCertificate: CN=Scott Hanselman, O=Scott Hanselman, ...
56+
# Status: Valid
57+
# StatusMessage: Signature verified
58+
```
59+
60+
## GitHub Actions Signing
61+
62+
### GitHub Secrets Required
63+
Repository secrets configured in Settings → Secrets and variables → Actions:
64+
- `AZURE_CLIENT_ID` - Service principal client ID
65+
- `AZURE_CLIENT_SECRET` - Service principal secret
66+
- `AZURE_TENANT_ID` - Azure tenant ID
67+
- `AZURE_SUBSCRIPTION_ID` - Azure subscription ID
68+
69+
### Workflow Integration
70+
The `.github/workflows/build.yml` workflow automatically signs executables on release:
71+
72+
```yaml
73+
- name: Azure Login
74+
uses: azure/login@v2
75+
with:
76+
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
77+
78+
- name: Sign executables with Trusted Signing
79+
uses: azure/trusted-signing-action@v0
80+
with:
81+
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
82+
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
83+
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
84+
endpoint: https://wus2.codesigning.azure.net/
85+
trusted-signing-account-name: <your-account-name>
86+
certificate-profile-name: <your-certificate-profile-name>
87+
files-folder: ${{ github.workspace }}\WindowsEdgeLight\bin\Release\net10.0-windows
88+
files-folder-filter: exe
89+
files-folder-recurse: true
90+
file-digest: SHA256
91+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
92+
timestamp-digest: SHA256
93+
```
94+
95+
### Triggering a Signed Release
96+
```powershell
97+
# Create and push a version tag
98+
git tag v1.10.1
99+
git push origin v1.10.1
100+
101+
# Or use the build script
102+
.\build.ps1 -Version "1.10.1"
103+
git tag v1.10.1
104+
git push origin v1.10.1
105+
```
106+
107+
## Troubleshooting
108+
109+
### Common Issues
110+
111+
#### 403 Forbidden Error
112+
- **Cause**: Missing permissions or wrong endpoint
113+
- **Solution**:
114+
- Verify user/service principal has "Trusted Signing Certificate Profile Signer" role
115+
- Use correct regional endpoint (West US 2: `https://wus2.codesigning.azure.net/`)
116+
- Check account name matches exactly (case-sensitive)
117+
118+
#### Authentication Failed - "Please run 'az login'"
119+
- **Cause**: Not logged in with correct scope
120+
- **Solution**:
121+
```powershell
122+
az logout
123+
az login --use-device-code --scope "https://codesigning.azure.net/.default"
124+
```
125+
126+
#### "User account does not exist in tenant 'Microsoft Services'"
127+
- **Cause**: Azure CLI credential trying to use Visual Studio credential which fails
128+
- **Solution**: Login with device code flow (see above)
129+
130+
#### GitHub Actions: 403 Error
131+
- **Cause**: Service principal missing permissions or secrets misconfigured
132+
- **Solution**:
133+
- Verify all 4 GitHub secrets are set correctly
134+
- Ensure service principal has "Trusted Signing Certificate Profile Signer" role
135+
- Check subscription ID matches the resource location
136+
137+
### Certificate Validity
138+
Azure Trusted Signing certificates are short-lived (typically 3 days). The timestamp ensures signatures remain valid after certificate expiration.
139+
140+
## Cost
141+
Azure Trusted Signing pricing (as of 2025):
142+
- **Public Trust Certificate Profile**: ~$9.99/month
143+
- **Signing Operations**: First 5,000 operations/month included
144+
- For occasional releases: ~$10-15/month
145+
146+
## Resources
147+
- [Azure Trusted Signing Documentation](https://learn.microsoft.com/en-us/azure/trusted-signing/)
148+
- [azure/trusted-signing-action](https://github.com/Azure/trusted-signing-action)
149+
- [dotnet/sign Tool](https://github.com/dotnet/sign)
150+
- [Issue #11 - SmartScreen Warning](https://github.com/shanselman/WindowsEdgeLight/issues/11)
151+
152+
## Notes
153+
- Certificates expire in 3 days but timestamped signatures remain valid indefinitely
154+
- Sign operations are automatic on tagged releases via GitHub Actions
155+
- Local signing requires Azure CLI authentication with codesigning scope
156+
- Both x64 and ARM64 executables are signed

0 commit comments

Comments
 (0)