Skip to content

Commit 2df1433

Browse files
committed
use azure for sign windows installer
1 parent 89ca783 commit 2df1433

2 files changed

Lines changed: 84 additions & 58 deletions

File tree

.github/workflows/build-electron.yml

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ concurrency:
1919

2020
permissions:
2121
contents: read
22+
id-token: write
2223

2324
env:
2425
RUST_VERSION: stable
@@ -36,10 +37,12 @@ jobs:
3637
- platform: win
3738
os: windows-latest
3839
artifact: chia-gaming-electron-windows
40+
environment: windows-code-signing
3941
- platform: linux
4042
os: ubuntu-22.04
4143
artifact: chia-gaming-electron-linux
4244
runs-on: ${{ matrix.os }}
45+
environment: ${{ matrix.environment }}
4346

4447
steps:
4548
- name: Checkout code
@@ -126,72 +129,56 @@ jobs:
126129
id: windows-signing
127130
shell: bash
128131
env:
129-
SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }}
130-
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
131-
SM_HOST: ${{ secrets.SM_HOST }}
132-
SM_API_KEY: ${{ secrets.SM_API_KEY }}
133-
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}
134-
SM_CERTKEY_ALIAS: ${{ secrets.SM_CERTKEY_ALIAS }}
132+
AZURE_SIGNING_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
133+
AZURE_SIGNING_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
135134
run: |
136135
configured=0
137136
for value in \
138-
"$SM_CLIENT_CERT_FILE_B64" \
139-
"$SM_CLIENT_CERT_PASSWORD" \
140-
"$SM_HOST" \
141-
"$SM_API_KEY" \
142-
"$SM_CODE_SIGNING_CERT_SHA1_HASH" \
143-
"$SM_CERTKEY_ALIAS"; do
137+
"$AZURE_SIGNING_CLIENT_ID" \
138+
"$AZURE_SIGNING_TENANT_ID"; do
144139
if [[ -n "$value" ]]; then
145140
configured=$((configured + 1))
146141
fi
147142
done
148143
149-
if [[ "$configured" -eq 6 ]]; then
144+
if [[ "$configured" -eq 2 ]]; then
150145
echo "available=true" >> "$GITHUB_OUTPUT"
151146
elif [[ "$configured" -eq 0 ]]; then
152147
echo "available=false" >> "$GITHUB_OUTPUT"
153148
else
154-
echo "::error::Windows signing is partially configured; all six DigiCert secrets are required"
149+
echo "::error::Windows signing is partially configured; both Azure signing secrets are required"
155150
exit 1
156151
fi
157152
158-
- name: Configure DigiCert Signing Manager
153+
- name: Install Azure Artifact Signing client
159154
if: steps.windows-signing.outputs.available == 'true'
160-
shell: bash
161-
env:
162-
SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }}
163-
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
164-
SM_HOST: ${{ secrets.SM_HOST }}
165-
SM_API_KEY: ${{ secrets.SM_API_KEY }}
166-
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}
155+
shell: pwsh
167156
run: |
168-
cert_file="D:/Certificate_pkcs12.p12"
169-
echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > /d/Certificate_pkcs12.p12
170-
{
171-
echo "SM_HOST=$SM_HOST"
172-
echo "SM_API_KEY=$SM_API_KEY"
173-
echo "SM_CLIENT_CERT_FILE=$cert_file"
174-
echo "SM_CLIENT_CERT_PASSWORD=$SM_CLIENT_CERT_PASSWORD"
175-
echo "SM_CODE_SIGNING_CERT_SHA1_HASH=$SM_CODE_SIGNING_CERT_SHA1_HASH"
176-
} >> "$GITHUB_ENV"
177-
{
178-
echo "C:/Program Files (x86)/Windows Kits/10/App Certification Kit"
179-
echo "C:/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.8 Tools"
180-
echo "C:/Program Files/DigiCert/DigiCert One Signing Manager Tools"
181-
} >> "$GITHUB_PATH"
182-
183-
- name: Install DigiCert Signing Manager tools
184-
if: steps.windows-signing.outputs.available == 'true'
185-
shell: cmd
186-
run: |
187-
curl -X GET https://download.chia.net/dc/smtools-windows-x64.msi -o smtools-windows-x64.msi
188-
start "" /wait msiexec.exe /i smtools-windows-x64.msi /qn /norestart
189-
set "installer_exit=%ERRORLEVEL%"
190-
if not "%installer_exit%"=="0" if not "%installer_exit%"=="3010" exit /b %installer_exit%
191-
smksp_registrar.exe list
192-
smctl.exe keypair ls
193-
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
194-
smctl.exe windows certsync --keypair-alias=${{ secrets.SM_CERTKEY_ALIAS }}
157+
$toolsDir = Join-Path $env:RUNNER_TEMP "artifact-signing-client"
158+
New-Item -ItemType Directory -Path $toolsDir -Force | Out-Null
159+
Push-Location $toolsDir
160+
161+
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile nuget.exe
162+
.\nuget.exe install Microsoft.ArtifactSigning.Client -x -OutputDirectory .
163+
164+
$dlib = Get-ChildItem -Recurse -Filter "Azure.CodeSigning.Dlib.dll" |
165+
Where-Object { $_.FullName -match '[\\/]x64[\\/]' } |
166+
Select-Object -First 1
167+
if (-not $dlib) {
168+
throw "Azure.CodeSigning.Dlib.dll (x64) not found after installing Microsoft.ArtifactSigning.Client"
169+
}
170+
171+
$metadataPath = Join-Path $toolsDir "metadata.json"
172+
@{
173+
Endpoint = "https://wus2.codesigning.azure.net/"
174+
CodeSigningAccountName = "ChiaNetworkInc"
175+
CertificateProfileName = "ChiaNetworkInc"
176+
CorrelationId = "github-actions-$env:GITHUB_RUN_ID"
177+
} | ConvertTo-Json | Set-Content -Path $metadataPath
178+
179+
Add-Content -Path $env:GITHUB_ENV -Value "AZURE_CODE_SIGNING_DLIB=$($dlib.FullName)"
180+
Add-Content -Path $env:GITHUB_ENV -Value "AZURE_CODE_SIGNING_METADATA=$metadataPath"
181+
Pop-Location
195182
196183
- name: Build signed Electron installer
197184
if: steps.apple-signing.outputs.available == 'true'
@@ -206,6 +193,10 @@ jobs:
206193
shell: bash
207194
env:
208195
HAS_SIGNING_SECRET: 'true'
196+
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
197+
AZURE_TOKEN_CREDENTIALS: prod
198+
AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
199+
AZURE_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
209200
run: tools/build-electron.sh --platform=${{ matrix.platform }}
210201

211202
- name: Build unsigned Electron installer

desktop/scripts/package-app.mjs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// are sealed by the time they are copied back.
99

1010
import { spawnSync } from 'node:child_process';
11-
import { copyFileSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
11+
import { copyFileSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
1212
import { createRequire } from 'node:module';
1313
import { dirname, join, resolve } from 'node:path';
1414
import { tmpdir } from 'node:os';
@@ -60,33 +60,68 @@ function executablePaths(directory) {
6060
}
6161

6262
function signAndVerify(path) {
63-
const certificateHash = process.env.SM_CODE_SIGNING_CERT_SHA1_HASH;
64-
if (!certificateHash) {
65-
throw new Error('SM_CODE_SIGNING_CERT_SHA1_HASH is required for Windows signing');
63+
const dlib = process.env.AZURE_CODE_SIGNING_DLIB;
64+
const metadata = process.env.AZURE_CODE_SIGNING_METADATA;
65+
if (!dlib || !metadata) {
66+
throw new Error('Azure Artifact Signing client and metadata are required for Windows signing');
6667
}
6768
run('signtool.exe', [
6869
'sign',
69-
'/sha1',
70-
certificateHash,
70+
'/v',
71+
'/fd',
72+
'SHA256',
7173
'/tr',
72-
'http://timestamp.digicert.com',
74+
'http://timestamp.acs.microsoft.com',
7375
'/td',
7476
'SHA256',
75-
'/fd',
76-
'SHA256',
77+
'/dlib',
78+
dlib,
79+
'/dmdf',
80+
metadata,
7781
path,
7882
]);
7983
run('signtool.exe', ['verify', '/v', '/pa', path]);
8084
}
8185

86+
async function requestAzureFederatedToken() {
87+
const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
88+
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
89+
if (!requestUrl || !requestToken) {
90+
throw new Error('GitHub OIDC token is unavailable; permissions.id-token must be write');
91+
}
92+
if (!process.env.AZURE_TENANT_ID || !process.env.AZURE_CLIENT_ID) {
93+
throw new Error('AZURE_TENANT_ID and AZURE_CLIENT_ID are required for Windows signing');
94+
}
95+
96+
const tokenUrl = new URL(requestUrl);
97+
tokenUrl.searchParams.set('audience', 'api://AzureADTokenExchange');
98+
const response = await fetch(tokenUrl, {
99+
headers: { Authorization: `Bearer ${requestToken}` },
100+
});
101+
if (!response.ok) {
102+
throw new Error(`GitHub OIDC token request failed with status ${response.status}`);
103+
}
104+
105+
const body = await response.json();
106+
if (!body || typeof body !== 'object' || typeof body.value !== 'string' || !body.value) {
107+
throw new Error('GitHub OIDC token response did not contain a token');
108+
}
109+
110+
const tokenFile = join(tmpdir(), 'azure-federated-token');
111+
writeFileSync(tokenFile, body.value, { mode: 0o600 });
112+
process.env.AZURE_FEDERATED_TOKEN_FILE = tokenFile;
113+
}
114+
82115
const builderArgs = process.argv.slice(2);
83116
if (WINDOWS_SIGNING) {
84117
electronBuilder([...builderArgs, '--dir']);
85118
const unpackedDirectory = join(BUILD_DIR, 'win-unpacked');
119+
await requestAzureFederatedToken();
86120
for (const path of executablePaths(unpackedDirectory)) {
87121
signAndVerify(path);
88122
}
89123
electronBuilder([...builderArgs, '--prepackaged', unpackedDirectory]);
124+
await requestAzureFederatedToken();
90125
for (const path of executablePaths(BUILD_DIR)) {
91126
if (dirname(path) === BUILD_DIR) {
92127
signAndVerify(path);

0 commit comments

Comments
 (0)