AD2022: SignSecurely¶
Summary¶
| Property | Value |
|---|---|
| ID | AD2022 |
| Name | SignSecurely |
| Category | Security |
| Severity | Error |
| Applies to | PE (Windows) |
Description¶
Binaries should be signed using secure cryptographic algorithms. This rule verifies that if a binary is signed, it uses modern, secure signing algorithms rather than deprecated ones like SHA-1.
How It Works¶
The rule examines:
- Authenticode signature presence and validity
- Hash algorithm used (SHA-256 or better required)
- Certificate chain and timestamp validity
- Dual-signing for compatibility when needed
Why This Matters¶
Code signing with weak algorithms provides false security assurance and may be bypassable by attackers.
Algorithm Security¶
| Algorithm | Status | Recommendation |
|---|---|---|
| MD5 | Broken | Never use |
| SHA-1 | Deprecated | Do not use |
| SHA-256 | Secure | Required minimum |
| SHA-384/512 | Secure | Acceptable |
SHA-1 Deprecation Timeline¶
| Date | Event |
|---|---|
| 2017 | First practical SHA-1 collision (SHAttered) |
| 2019 | Windows stops trusting SHA-1 for new certs |
| 2020 | Chosen-prefix collision demonstrated |
| 2021+ | SHA-1 signed binaries increasingly blocked |
The Collision Attack¶
SHA-1 collision allows:
1. Attacker creates malicious binary
2. Crafts it to have same SHA-1 hash as legitimate binary
3. Signature validates for malicious binary
4. Users trust malicious code
Signing Best Practices¶
| Practice | Reason |
|---|---|
| SHA-256 or better | Collision resistant |
| Timestamp signature | Remains valid after cert expiry |
| EV certificates | Higher trust, SmartScreen reputation |
| HSM key storage | Protects signing key |
Dual Signing for Compatibility¶
# Sign with both SHA-1 (legacy) and SHA-256 (secure)
signtool sign /sha1 THUMBPRINT /fd sha256 /tr http://timestamp.url /td sha256 binary.exe
signtool sign /sha1 THUMBPRINT /as /fd sha1 /tr http://timestamp.url /td sha1 binary.exe
Performance and Resolution¶
Performance Considerations¶
Secure code signing has minimal performance impact:
| Operation | Impact |
|---|---|
| Signature verification (OS loader) | ~5-10ms at load time |
| SHA-256 vs SHA-1 verification | Negligible difference |
| Timestamping overhead | Build-time only |
| Binary size increase | ~2-5KB for signature |
The security benefits far outweigh the minimal overhead. Windows caches signature verification results, so subsequent loads are faster.
Resolution Steps¶
1. Re-sign with SHA-256¶
Using Windows SDK signtool:
# Sign with SHA-256 using certificate from store
signtool sign /sha1 <CERT_THUMBPRINT> /fd sha256 /tr http://timestamp.digicert.com /td sha256 binary.exe
# Or using PFX file
signtool sign /f certificate.pfx /p <password> /fd sha256 /tr http://timestamp.digicert.com /td sha256 binary.exe
2. Dual-Sign for Legacy Compatibility¶
If you must support Windows Vista/7 without updates:
# First signature: SHA-256 (primary, for modern systems)
signtool sign /sha1 <CERT_THUMBPRINT> /fd sha256 /tr http://timestamp.digicert.com /td sha256 binary.exe
# Second signature: SHA-1 (appended, for legacy systems)
signtool sign /sha1 <CERT_THUMBPRINT> /as /fd sha1 /t http://timestamp.digicert.com binary.exe
3. Verify Signature Algorithm¶
# Check signature details
signtool verify /pa /v binary.exe
# Look for "Hash of file (sha256):" in output
# PowerShell verification
Get-AuthenticodeSignature binary.exe | Select-Object -ExpandProperty SignerCertificate | Format-List
Timestamp Server URLs¶
Always timestamp signatures to ensure validity after certificate expiration:
| Provider | SHA-256 Timestamp URL |
|---|---|
| DigiCert | http://timestamp.digicert.com |
| Sectigo | http://timestamp.sectigo.com |
| GlobalSign | http://timestamp.globalsign.com/tsa/r6advanced1 |
| SSL.com | http://ts.ssl.com |
HSM and Secure Key Storage¶
For production signing, use Hardware Security Modules:
# Sign using HSM-stored certificate (Azure Key Vault example)
AzureSignTool sign -kvu https://myvault.vault.azure.net `
-kvc MyCertificateName `
-fd sha256 `
-tr http://timestamp.digicert.com `
binary.exe
Certificate Best Practices¶
| Practice | Implementation |
|---|---|
| EV Certificate | Higher SmartScreen trust, protected key |
| HSM Storage | Azure Key Vault, AWS CloudHSM, on-prem HSM |
| Key Rotation | New certificate before expiry |
| Access Control | Limit signing key access to CI/CD only |
CI/CD Integration¶
Azure DevOps:
- task: DotNetCoreCLI@2
displayName: 'Sign binaries'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'run sign --file-digest sha256 --timestamp-url http://timestamp.digicert.com'
GitHub Actions with Azure Key Vault:
- name: Sign binaries
uses: azure/azure-code-signing-action@v0.3.0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: https://wus.codesigning.azure.net/
code-signing-account-name: my-signing-account
certificate-profile-name: my-cert-profile
files-folder: ./build/output
files-folder-filter: exe,dll
file-digest: SHA256
timestamp-rfc3161: http://timestamp.digicert.com
Verification Script¶
# Batch verify all signed binaries
Get-ChildItem -Path .\build\output -Include *.exe,*.dll -Recurse | ForEach-Object {
$sig = Get-AuthenticodeSignature $_.FullName
if ($sig.Status -ne 'Valid') {
Write-Error "Invalid signature: $($_.Name)"
exit 1
}
if ($sig.SignerCertificate.SignatureAlgorithm.FriendlyName -match 'sha1') {
Write-Warning "SHA-1 signature detected: $($_.Name)"
}
}