Last modified: January 24, 2026

This article is written in: πŸ‡ΊπŸ‡Έ

Windows Code Signing

This document describes the Windows code signing implementation for Standard of Iron.

Overview

The Windows build pipeline automatically signs the .exe file with Authenticode using an organization EV (Extended Validation) certificate. Code signing provides:

Implementation

Build Pipeline

The code signing step is integrated into the Windows build workflow (.github/workflows/windows.yml) and runs automatically when tags are pushed (e.g., v1.2.3).

The signing process: 1. Runs after the build step completes 2. Only executes if the WINDOWS_CERTIFICATE secret is configured 3. Signs the main executable (standard_of_iron.exe) 4. Uses SHA-256 algorithm for both file digest and timestamp 5. Timestamps the signature using DigiCert's timestamp server 6. Verifies the signature after signing

Signed Files

Currently, the pipeline signs: - standard_of_iron.exe - The main game executable

Note: Qt DLLs and other third-party libraries are not signed by this pipeline as they should already be signed by their respective publishers (e.g., Qt Company).

Setup Instructions

Prerequisites

  1. EV Code Signing Certificate: Obtain an Extended Validation code signing certificate from a trusted Certificate Authority (CA) such as:
  2. DigiCert
  3. Sectigo
  4. GlobalSign
  5. Entrust

  6. Certificate Format: Export the certificate as a .pfx (PKCS#12) file with a strong password

GitHub Secrets Configuration

To enable code signing in GitHub Actions, configure the following repository secrets:

  1. WINDOWS_CERTIFICATE (Required)
  2. Navigate to: Repository Settings β†’ Secrets and variables β†’ Actions β†’ New repository secret
  3. Name: WINDOWS_CERTIFICATE
  4. Value: Base64-encoded content of your .pfx certificate file

To encode your certificate on Linux/macOS:

base64 -i certificate.pfx | tr -d '\n' > certificate.txt
   cat certificate.txt

On Windows PowerShell:

$certBytes = [System.IO.File]::ReadAllBytes("certificate.pfx")
   $certBase64 = [System.Convert]::ToBase64String($certBytes)
   $certBase64 | Out-File -FilePath certificate.txt -NoNewline
   Get-Content certificate.txt

Copy the entire base64 string and paste it as the secret value.

  1. WINDOWS_CERTIFICATE_PASSWORD (Required)
  2. Name: WINDOWS_CERTIFICATE_PASSWORD
  3. Value: The password used to protect the .pfx certificate file

Verification

After pushing a tag and completing the build:

  1. Download the Windows artifact from GitHub Actions
  2. Extract the .exe file
  3. Right-click the .exe β†’ Properties β†’ Digital Signatures tab
  4. Verify that:
  5. The signature shows your organization name
  6. The signature is valid
  7. The timestamp is present and valid

Alternatively, use signtool to verify from the command line:

signtool verify /pa /v standard_of_iron.exe

Security Considerations

  1. Certificate Protection:
  2. Store the certificate securely
  3. Use a strong password
  4. Limit access to the certificate and password
  5. Use GitHub's encrypted secrets feature (never commit certificates to the repository)

  6. Certificate Expiration:

  7. Monitor certificate expiration dates
  8. Plan for renewal well in advance
  9. Update the GitHub secret when renewing

  10. Timestamping:

  11. The signature includes a trusted timestamp via RFC 3161 protocol
  12. Timestamp URL uses HTTP (not HTTPS) as per RFC 3161 standard - the timestamp response is cryptographically signed
  13. Signatures remain valid even after the certificate expires (as long as signed before expiration)
  14. If the timestamp server is unavailable, signing will fail (this is expected behavior)

Troubleshooting

Signing Step Skipped

If the signing step is skipped, verify: - The WINDOWS_CERTIFICATE secret is configured - The secret name matches exactly (case-sensitive) - The workflow has permission to access secrets

Signing Fails

If signing fails, check: 1. Certificate is valid and not expired 2. Password is correct 3. Certificate is in .pfx format 4. Base64 encoding is correct (no line breaks or extra characters) 5. Timestamp server (http://timestamp.digicert.com) is accessible

SmartScreen Still Shows Warnings

Even with a valid signature, SmartScreen may show warnings if: - The certificate is new and hasn't built reputation yet - The executable hasn't been downloaded by many users yet - The signature is from a standard (non-EV) certificate

EV certificates typically bypass these warnings immediately, but standard certificates may require time to build reputation.

Future Enhancements

Potential improvements for the code signing implementation:

  1. Sign Custom DLLs: If the project adds custom DLL libraries, include them in the signing process
  2. Dual Signing: Add SHA-1 signatures for older Windows versions (Windows 7, 8)
  3. Azure SignTool: Migrate to Azure Key Vault for certificate storage if using Azure DevOps
  4. Signature Verification: Add automated signature verification to CI/CD pipeline

References