From 853908bedd749b84843ab550fae244254914c34f Mon Sep 17 00:00:00 2001 From: X9 Dev Date: Fri, 29 May 2026 15:00:12 +0200 Subject: [PATCH] ci: sign xetup.exe via Azure Trusted Signing (jsign) Add a signing step after the build that authenticates the Entra service principal (client_credentials), fetches a Trusted Signing access token, and signs xetup.exe with jsign using the X9.cz s.r.o. certificate profile plus an RFC3161 timestamp (timestamp.acs.microsoft.com). jsign is pinned by version and sha256. Trusted Signing certs are short-lived (~3 days); the timestamp keeps the signature valid past expiry, so timestamping must succeed and the step fails hard otherwise. Only AZURE_CLIENT_SECRET needs to be set as a Forgejo Actions secret; the non-secret identifiers are inlined in the workflow. gitignore the local manual-signing helpers (sign*.sh) and the *.unsigned build backup. Co-Authored-By: Claude Opus 4.8 --- .forgejo/workflows/release.yml | 50 ++++++++++++++++++++++++++++++++++ .gitignore | 5 ++++ 2 files changed, 55 insertions(+) diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index f88e09b..d85d51a 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -46,6 +46,56 @@ jobs: go build -ldflags="-s -w -H windowsgui" -o xetup.exe ./cmd/xetup/ echo "Built: $(ls -lh xetup.exe | awk '{print $5}')" + - name: Sign xetup.exe (Azure Trusted Signing) + env: + # Non-secret identifiers (Entra app + signing account) - safe to inline. + # Only the client secret is a Forgejo secret (Settings > Actions > Secrets). + AZURE_TENANT_ID: 7d36c38a-f04e-49b4-b500-b1677a7fe62f + AZURE_CLIENT_ID: a96e36b5-2661-497a-9d16-b70a6096e78b + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + TS_ENDPOINT: weu.codesigning.azure.net + TS_ACCOUNT: x9-signing + TS_PROFILE: x9-public + TS_TSA: http://timestamp.acs.microsoft.com + JSIGN_VERSION: "7.4" + JSIGN_SHA256: 2abf2ade9ea322acc2d60c24794eadc465ff9380938fca4c932d09e0b25f1c28 + run: | + if [ -z "$AZURE_CLIENT_SECRET" ]; then + echo "ERROR: AZURE_CLIENT_SECRET not set (Forgejo > repo Settings > Actions > Secrets)" >&2 + exit 1 + fi + apk add --no-cache openjdk17-jre-headless + + # Fetch jsign - pinned version, sha256-verified (supply-chain guard) + curl -fsSL -o /tmp/jsign.jar \ + "https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar" + echo "${JSIGN_SHA256} /tmp/jsign.jar" | sha256sum -c - + + # Acquire short-lived Trusted Signing access token from the service principal + TOKEN=$(curl -fsS -X POST \ + "https://login.microsoftonline.com/${AZURE_TENANT_ID}/oauth2/v2.0/token" \ + -d grant_type=client_credentials \ + -d "client_id=${AZURE_CLIENT_ID}" \ + --data-urlencode "client_secret=${AZURE_CLIENT_SECRET}" \ + --data-urlencode "scope=https://codesigning.azure.net/.default" \ + | jq -r '.access_token') + [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ] || { echo "ERROR: token acquisition failed" >&2; exit 1; } + echo "Trusted Signing token acquired (length ${#TOKEN})" + + # Sign + RFC3161 timestamp. The signing cert is short-lived (~3 days); + # the timestamp is what keeps the signature valid after it expires, so + # timestamping must succeed - the step fails hard if it does not. + java -jar /tmp/jsign.jar \ + --storetype TRUSTEDSIGNING \ + --keystore "${TS_ENDPOINT}" \ + --storepass "${TOKEN}" \ + --alias "${TS_ACCOUNT}/${TS_PROFILE}" \ + --tsaurl "${TS_TSA}" \ + --tsmode RFC3161 \ + --alg SHA-256 \ + xetup.exe + echo "Signed and timestamped xetup.exe" + - name: Publish latest release env: TOKEN: ${{ secrets.FORGEJO_TOKEN }} diff --git a/.gitignore b/.gitignore index 35f86e8..eea0811 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,11 @@ Desktop.ini # Build artifacts flash.zip +# Signing (local manual-signing helpers + unsigned build backup) +sign.sh +sign-azlogin.sh +*.unsigned + # Large reference files W11.pdf