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