# Description of Changes Ci/environments cleanups, new envs and master to release naming --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
8.4 KiB
Windows Code Signing Setup Guide
This guide explains how to set up Windows code signing for Stirling-PDF desktop application builds.
Overview
Releases are signed with DigiCert KeyLocker, a cloud HSM: the private key never
leaves DigiCert, and the runner signs through a PKCS#11 provider. The older approach
of uploading a base64 .pfx to a repository secret has been removed from the
workflows - the sections below describe KeyLocker, which is what actually runs.
Windows code signing is essential for:
- Preventing Windows SmartScreen warnings
- Building trust with users
- Enabling Microsoft Store distribution
- Professional application distribution
Certificate Types
OV Certificate (Organization Validated)
- More affordable option
- Requires business verification
- May trigger SmartScreen warnings initially until reputation builds
- Suitable for most independent software vendors
EV Certificate (Extended Validation)
- Premium option with immediate SmartScreen reputation
- Requires hardware security module (HSM) or cloud-based signing
- Higher cost but provides immediate trust
- Required since June 2023 for new certificates
Obtaining a Certificate
Certificate Authorities
Popular certificate authorities for Windows code signing:
- DigiCert
- Sectigo (formerly Comodo)
- GlobalSign
- SSL.com
Certificate Format
You'll receive a certificate in one of these formats:
.pfxor.p12(preferred - contains both certificate and private key).cer+ private key (needs conversion to .pfx)
Converting to PFX (if needed)
If you have separate certificate and private key files:
openssl pkcs12 -export -out certificate.pfx -inkey private-key.key -in certificate.cer
Setting Up GitHub Secrets
Required Secrets
Navigate to your GitHub repository → Settings → Environments → release-signing.
These live in the release-signing environment, not at repository scope. That
environment requires reviewer approval and is limited to main, release,
hotfix/* and v* tags. All five come from the DigiCert ONE console.
| Secret | Description |
|---|---|
SM_API_KEY |
KeyLocker API key. Also acts as the on/off switch: signing steps are gated on it being non-empty. |
SM_CLIENT_CERT_FILE_B64 |
Base64-encoded PKCS#12 client authentication certificate. |
SM_CLIENT_CERT_PASSWORD |
Password for that client certificate. |
SM_KEYPAIR_ALIAS |
Alias of the signing keypair to use. |
SM_HOST |
DigiCert ONE host, e.g. https://clientauth.one.digicert.com. |
Optional Secrets for Tauri Updater
If you're using Tauri's built-in updater feature:
TAURI_SIGNING_PRIVATE_KEY
- Generated using Tauri CLI:
npm run tauri signer generate - Used for update package verification
TAURI_SIGNING_PRIVATE_KEY_PASSWORD
- Password for the Tauri signing key
Configuration Files
1. Tauri Configuration (frontend/editor/src-tauri/tauri.conf.json)
The Windows signing configuration is already set up:
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
Configuration Options:
certificateThumbprint: Automatically extracted from imported certificate (leave asnull)digestAlgorithm: Hashing algorithm -sha256is recommendedtimestampUrl: Timestamp server to prove signing time (survives certificate expiration)
Alternative Timestamp Servers:
- DigiCert:
http://timestamp.digicert.com - Sectigo:
http://timestamp.sectigo.com - GlobalSign:
http://timestamp.globalsign.com
2. GitHub Workflow (.github/workflows/tauri-build.yml)
The workflow includes four Windows signing steps, all gated on SM_API_KEY being
set and the ref being the release branch:
- Setup DigiCert KeyLocker: Installs the DigiCert signing tools via
digicert/ssm-code-signing - Setup DigiCert KeyLocker Certificate: Writes the client cert and exports the PKCS#11 config
- Configure Windows code signing / Build Tauri app: Signs through the PKCS#11 provider
- Verify Windows Code Signature: Validates that the .exe and .msi are properly signed
Testing the Setup
1. Local Testing (Windows Only)
KeyLocker is CI-only. To check signing locally, install your own certificate into the Windows store and point Tauri at it; the build no longer reads any certificate from an environment variable.
# Build the application
cd frontend
npm run tauri build
# Verify the signature
Get-AuthenticodeSignature "./src-tauri/target/release/bundle/msi/Stirling-PDF_*.msi"
2. GitHub Actions Testing
- Push your changes to a branch
- Manually trigger the workflow:
- Go to Actions → Build Tauri Applications
- Click "Run workflow"
- Select "windows" platform
- Check the build logs for:
- ✅ Certificate import success
- ✅ Build completion
- ✅ Signature verification
3. Verifying Signed Binaries
After downloading the built artifacts:
Windows (PowerShell):
Get-AuthenticodeSignature "Stirling-PDF-windows-x86_64.exe"
Get-AuthenticodeSignature "Stirling-PDF-windows-x86_64.msi"
Look for:
- Status:
Valid - Signer: Your organization name
- Timestamp: Recent date/time
Windows (GUI):
- Right-click the .exe or .msi file
- Select "Properties"
- Go to "Digital Signatures" tab
- Verify signature details
Troubleshooting
"HashMismatch" Status
- Certificate doesn't match the binary
- Possible file corruption during download
- Re-download and verify
"NotSigned" Status
- Certificate wasn't imported correctly
- Check GitHub secrets are set correctly
- Verify base64 encoding is complete (no truncation)
"UnknownError" Status
- Timestamp server unreachable
- Try alternative timestamp URL in tauri.conf.json
- Check network connectivity in GitHub Actions
SmartScreen Still Shows Warnings
- Normal for OV certificates initially
- Reputation builds over time with user downloads
- Consider EV certificate for immediate reputation
Certificate Not Found During Build
- Verify
SM_API_KEYis present in therelease-signingenvironment. If it is empty the signing steps skip silently and the build succeeds unsigned. - Check
SM_CLIENT_CERT_FILE_B64base64 encoding is correct (no extra whitespace) - Ensure
SM_CLIENT_CERT_PASSWORDandSM_KEYPAIR_ALIASmatch the DigiCert keypair
Security Best Practices
-
Never commit certificates to version control
- Keep .pfx files secure and backed up
- Use GitHub secrets for CI/CD
-
Rotate certificates before expiration
- Set calendar reminders
- Update GitHub secrets with new certificate
-
Use strong passwords
- Certificate password should be complex
- Store securely (password manager)
-
Monitor certificate usage
- Review GitHub Actions logs
- Set up notifications for failed builds
-
Limit access to secrets
- Only repository admins should access secrets
- Audit secret access regularly
Certificate Lifecycle
Before Expiration
- Renew the certificate in the DigiCert ONE console (typically annual)
- If the keypair alias changed, update
SM_KEYPAIR_ALIASin therelease-signingenvironment - If the client authentication certificate was reissued, update
SM_CLIENT_CERT_FILE_B64andSM_CLIENT_CERT_PASSWORD - Test build to verify the new certificate works
Expired Certificates
- Signed binaries remain valid (timestamp proves signing time)
- New builds will fail until certificate is renewed
- Users can still install previously signed versions
Cost Considerations
Certificate Costs (Annual, as of 2024)
- OV Certificate: $100-400/year
- EV Certificate: $400-1000/year
Choosing the Right Certificate
- Open source / early stage: Start with OV
- Commercial / enterprise: Consider EV for better trust
- Microsoft Store: EV certificate required
Additional Resources
- Tauri Windows Signing Documentation
- Microsoft Code Signing Overview
- DigiCert Code Signing Guide
- Windows SmartScreen FAQ
Support
If you encounter issues with Windows code signing:
- Check GitHub Actions logs for detailed error messages
- Verify all secrets are set correctly
- Test certificate locally first (Windows environment required)
- Open an issue in the repository with relevant logs (remove sensitive data)