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:
- Verified publisher: Windows displays the organization name as a verified publisher
- Reduced SmartScreen friction: Signed executables are less likely to trigger Windows Defender SmartScreen warnings
- Trust and authenticity: Users can verify the software comes from the legitimate publisher
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:
- Runs after the build step completes
- Only executes if the
WINDOWS_CERTIFICATE secret is configured
- Signs the main executable (
standard_of_iron.exe)
- Uses SHA-256 algorithm for both file digest and timestamp
- Timestamps the signature using DigiCert's timestamp server
- 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
EV Code Signing Certificate: Obtain an Extended Validation code signing certificate from a trusted Certificate Authority (CA) such as:
- DigiCert
- Sectigo
- GlobalSign
- Entrust
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:
- WINDOWS_CERTIFICATE (Required)
- Navigate to: Repository Settings → Secrets and variables → Actions → New repository secret
- Name:
WINDOWS_CERTIFICATE
- 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.
- WINDOWS_CERTIFICATE_PASSWORD (Required)
- Name:
WINDOWS_CERTIFICATE_PASSWORD
- Value: The password used to protect the
.pfx certificate file
Verification
After pushing a tag and completing the build:
- Download the Windows artifact from GitHub Actions
- Extract the
.exe file
- Right-click the
.exe → Properties → Digital Signatures tab
- Verify that:
- The signature shows your organization name
- The signature is valid
- 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
Certificate Protection:
- Store the certificate securely
- Use a strong password
- Limit access to the certificate and password
- Use GitHub's encrypted secrets feature (never commit certificates to the repository)
Certificate Expiration:
- Monitor certificate expiration dates
- Plan for renewal well in advance
- Update the GitHub secret when renewing
Timestamping:
- The signature includes a trusted timestamp via RFC 3161 protocol
- Timestamp URL uses HTTP (not HTTPS) as per RFC 3161 standard - the timestamp response is cryptographically signed
- Signatures remain valid even after the certificate expires (as long as signed before expiration)
- 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:
- Certificate is valid and not expired
- Password is correct
- Certificate is in
.pfx format
- Base64 encoding is correct (no line breaks or extra characters)
- 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:
- Sign Custom DLLs: If the project adds custom DLL libraries, include them in the signing process
- Dual Signing: Add SHA-1 signatures for older Windows versions (Windows 7, 8)
- Azure SignTool: Migrate to Azure Key Vault for certificate storage if using Azure DevOps
- Signature Verification: Add automated signature verification to CI/CD pipeline
References