Code Signing Skill
Code Signing Skill
When to Use This Skill
Use this skill when implementing code signing patterns in your codebase.
How to Use This Skill
- Review the patterns and examples below
- Apply the relevant patterns to your implementation
- Follow the best practices outlined in this skill
Overview
Code signing ensures binary authenticity and integrity, enabling operating systems and users to verify that software comes from a trusted source and hasn't been modified.
Quick Reference
Platform Commands
| Platform | Tool | Sign Command | Verify Command |
|---|---|---|---|
| macOS | codesign | codesign --sign "Dev ID" binary | codesign --verify binary |
| Windows | SignTool | signtool sign /sha1 THUMB binary.exe | signtool verify /pa binary.exe |
| Linux | GPG | gpg --detach-sign binary | gpg --verify binary.asc binary |
Level 1: Essential Patterns
macOS Signing (Minimum Viable)
# Sign binary with Developer ID
codesign --sign "Developer ID Application: Company (TEAMID)" \
--options runtime \
--timestamp \
binary
# Verify signature
codesign --verify --verbose binary
Windows Signing (Minimum Viable)
# Sign with certificate thumbprint
signtool.exe sign `
/sha1 "CERTIFICATE_THUMBPRINT" `
/fd SHA256 `
/tr http://timestamp.digicert.com `
/td SHA256 `
binary.exe
# Verify signature
signtool.exe verify /pa binary.exe
Linux Signing (Minimum Viable)
# Create detached signature
gpg --armor --detach-sign binary
# Verify signature
gpg --verify binary.asc binary
Level 2: Production Patterns
macOS Complete Workflow with Notarization
#!/bin/bash
set -euo pipefail
BINARY="$1"
DEVELOPER_ID="${DEVELOPER_ID:-Developer ID Application: Your Company (TEAMID)}"
KEYCHAIN_PROFILE="${KEYCHAIN_PROFILE:-notarization-profile}"
echo "=== Signing $BINARY ==="
# Sign with hardened runtime (required for notarization)
codesign --sign "$DEVELOPER_ID" \
--options runtime \
--timestamp \
--force \
"$BINARY"
echo "=== Creating ZIP for notarization ==="
ZIPFILE="${BINARY}.zip"
zip -j "$ZIPFILE" "$BINARY"
echo "=== Submitting to Apple Notarization Service ==="
xcrun notarytool submit "$ZIPFILE" \
--keychain-profile "$KEYCHAIN_PROFILE" \
--wait
echo "=== Stapling notarization ticket ==="
xcrun stapler staple "$BINARY"
echo "=== Verification ==="
codesign --verify --verbose=4 "$BINARY"
spctl --assess --verbose=4 "$BINARY"
# Cleanup
rm -f "$ZIPFILE"
echo "=== Signing complete ==="
Windows Complete Workflow
param(
[Parameter(Mandatory=$true)]
[string]$BinaryPath,
[Parameter(Mandatory=$true)]
[string]$CertThumbprint,
[string]$TimestampServer = "http://timestamp.digicert.com",
[string]$Description = "Application"
)
$ErrorActionPreference = "Stop"
Write-Host "=== Signing $BinaryPath ===" -ForegroundColor Cyan
# Sign with SHA256 and RFC 3161 timestamp
$signArgs = @(
"sign",
"/sha1", $CertThumbprint,
"/fd", "SHA256",
"/tr", $TimestampServer,
"/td", "SHA256",
"/d", $Description,
"/v",
$BinaryPath
)
& signtool.exe @signArgs
if ($LASTEXITCODE -ne 0) {
throw "Signing failed with exit code $LASTEXITCODE"
}
Write-Host "=== Verifying signature ===" -ForegroundColor Cyan
& signtool.exe verify /pa /v $BinaryPath
if ($LASTEXITCODE -ne 0) {
throw "Verification failed with exit code $LASTEXITCODE"
}
Write-Host "=== Signing complete ===" -ForegroundColor Green
Linux Complete Workflow with Checksums
#!/bin/bash
set -euo pipefail
BINARY="$1"
GPG_KEY_ID="${GPG_KEY_ID:-}"
if [[ -z "$GPG_KEY_ID" ]]; then
echo "ERROR: GPG_KEY_ID environment variable required"
exit 1
fi
echo "=== Signing $BINARY ==="
# Create detached signature
gpg --default-key "$GPG_KEY_ID" \
--armor \
--detach-sign \
--output "${BINARY}.asc" \
"$BINARY"
echo "=== Creating signed checksums ==="
# Generate checksum
sha256sum "$BINARY" > "${BINARY}.sha256"
# Sign checksum file
gpg --default-key "$GPG_KEY_ID" \
--armor \
--detach-sign \
"${BINARY}.sha256"
echo "=== Verification ==="
gpg --verify "${BINARY}.asc" "$BINARY"
sha256sum --check "${BINARY}.sha256"
gpg --verify "${BINARY}.sha256.asc" "${BINARY}.sha256"
echo "=== Signing complete ==="
echo "Generated files:"
echo " - ${BINARY}.asc (detached signature)"
echo " - ${BINARY}.sha256 (checksum)"
echo " - ${BINARY}.sha256.asc (signed checksum)"
CI/CD Integration
GitHub Actions Secrets Required
# macOS
APPLE_CERTIFICATE: base64-encoded .p12 certificate
APPLE_CERTIFICATE_PASSWORD: certificate password
APPLE_ID: developer@company.com
APPLE_TEAM_ID: ABCD123456
APPLE_PASSWORD: app-specific password
APPLE_DEVELOPER_ID: "Developer ID Application: Company (TEAMID)"
# Windows
WINDOWS_CERT_BASE64: base64-encoded .pfx certificate
WINDOWS_CERT_PASSWORD: certificate password
WINDOWS_CERT_THUMBPRINT: SHA1 thumbprint
# Linux
GPG_PRIVATE_KEY: ASCII-armored private key
GPG_PASSPHRASE: key passphrase
GPG_KEY_ID: key ID or email
GitHub Actions Example
jobs:
sign:
strategy:
matrix:
include:
- os: macos-latest
platform: darwin
- os: windows-latest
platform: win32
- os: ubuntu-latest
platform: linux
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Sign (macOS)
if: matrix.platform == 'darwin'
run: ./scripts/sign-macos-binary.sh dist/app-darwin-*
- name: Sign (Windows)
if: matrix.platform == 'win32'
run: ./scripts/sign-windows-binary.ps1 -BinaryPath dist/app-win32-*.exe
- name: Sign (Linux)
if: matrix.platform == 'linux'
run: ./scripts/sign-linux-binary.sh dist/app-linux-*
Certificate Setup
macOS Notarization Profile Setup
# One-time setup: Store credentials in keychain
xcrun notarytool store-credentials "notarization-profile" \
--apple-id "developer@company.com" \
--team-id "ABCD123456" \
--password "@keychain:AC_PASSWORD"
Windows Certificate Import
# Import PFX to certificate store
$cert = Import-PfxCertificate `
-FilePath "certificate.pfx" `
-Password (ConvertTo-SecureString -String "password" -AsPlainText -Force) `
-CertStoreLocation Cert:\CurrentUser\My
# Get thumbprint
$cert.Thumbprint
Linux GPG Key Setup
# Generate new key (if needed)
gpg --full-generate-key
# List keys to get ID
gpg --list-secret-keys --keyid-format LONG
# Export for CI/CD (store as secret)
gpg --armor --export-secret-keys KEY_ID
Timestamp Servers
Always use timestamps for long-term validity:
| Provider | URL | Protocol |
|---|---|---|
| DigiCert | http://timestamp.digicert.com | RFC 3161 |
| Sectigo | http://timestamp.sectigo.com | RFC 3161 |
| GlobalSign | http://timestamp.globalsign.com/tsa/r6advanced1 | RFC 3161 |
| Comodo | http://timestamp.comodoca.com | Authenticode |
Troubleshooting
macOS Issues
"Developer cannot be verified"
- Ensure binary is notarized AND stapled
- Run:
xcrun stapler staple binary
Notarization fails with hardened runtime errors
- Check entitlements file
- Ensure all dependencies are signed
Windows Issues
"Unknown publisher" warning
- Use EV certificate for SmartScreen reputation
- Ensure timestamp is included
SignTool can't find certificate
- Verify certificate is in CurrentUser\My store
- Check thumbprint is correct (no spaces)
Linux Issues
GPG can't find key
- Verify key is imported:
gpg --list-secret-keys - Check GPG_KEY_ID matches actual key ID
Security Checklist
- Certificates stored in secure secrets management
- Timestamps included for all signatures
- Certificates rotated before expiration
- Minimum key length: RSA 2048 or ECC 256
- Hardware Security Module (HSM) for production
- Audit log of all signing operations
Success Output
When successful, this skill MUST output:
✅ SKILL COMPLETE: code-signing
Completed:
- [x] Platform-specific signing script created
- [x] Binary signed with valid certificate/key
- [x] Signature verified successfully
- [x] Timestamp added (for long-term validity)
- [x] Notarization completed (macOS only)
- [x] CI/CD secrets configured
- [x] Signed artifacts ready for distribution
Outputs (platform-specific):
- macOS: binary (signed + notarized + stapled)
- Windows: binary.exe (signed with SHA256)
- Linux: binary, binary.asc, binary.sha256, binary.sha256.asc
Verification:
- macOS: codesign --verify --verbose binary && spctl --assess binary
- Windows: signtool verify /pa binary.exe
- Linux: gpg --verify binary.asc binary && sha256sum --check binary.sha256
Completion Checklist
Before marking this skill as complete, verify:
- Signing certificate/key obtained and stored securely
- Signing script created for target platform
- Binary signed with appropriate algorithm (SHA256 minimum)
- Timestamp included in signature
- Signature verification passes locally
- macOS: Notarization submitted and approved
- macOS: Notarization ticket stapled to binary
- Windows: Certificate in correct store (CurrentUser\My)
- Linux: Detached signature (.asc) created
- Linux: Checksums (.sha256) generated and signed
- CI/CD secrets configured (certificate, passwords, API keys)
- Test signing in CI/CD pipeline
- Signed binaries distributed to users successfully
- End-user verification instructions documented
Failure Indicators
This skill has FAILED if:
- ❌ Signature verification fails (codesign/signtool/gpg error)
- ❌ macOS: "Developer cannot be verified" warning on user machine
- ❌ macOS: Notarization rejected by Apple
- ❌ Windows: "Unknown publisher" SmartScreen warning
- ❌ Linux: GPG cannot find public key for verification
- ❌ Certificate expired or revoked
- ❌ No timestamp included (signature expires with certificate)
- ❌ CI/CD pipeline fails to sign binaries
- ❌ Signing credentials exposed in repository
- ❌ Binary modified after signing (signature invalid)
When NOT to Use
Do NOT use code-signing when:
- Internal tools only - Distribution limited to trusted internal network
- Open-source scripts - Users can inspect source code directly
- Web applications - Use HTTPS/TLS instead (different trust model)
- Container images - Use image signing (cosign, Notary) instead
- Development/testing builds - Adds overhead without security benefit
- Rapid iteration phase - Wait until release candidates for signing
- No certificate available - Obtain certificate first (can take weeks)
Use HTTPS/TLS when: Web applications or APIs (different security mechanism) Use image signing when: Docker/OCI containers (cosign, Docker Content Trust) Use this skill when: Distributing binaries to end-users outside trusted network
Anti-Patterns (Avoid)
| Anti-Pattern | Problem | Solution |
|---|---|---|
| No timestamp | Signature expires with certificate | Always include timestamp from trusted server |
| Hardcoded credentials | Certificate/password in code repository | Use CI/CD secrets, never commit credentials |
| Self-signed certificates | Users see "Unknown publisher" warnings | Use certificate from trusted CA |
| SHA1 signing | Deprecated, insecure (collision attacks) | Use SHA256 or SHA512 minimum |
| No certificate rotation | Expired certificate stops distribution | Monitor expiration, renew 30 days early |
| Signing after upload | Binary modified between sign and distribute | Sign immediately before distribution |
| Missing verification | Broken signatures go unnoticed | Verify signature immediately after signing |
| No backup of certificate | Lost certificate prevents urgent releases | Securely backup certificate and password |
| Manual signing only | Bottleneck for releases, error-prone | Automate signing in CI/CD pipeline |
| Weak password protection | Certificate stolen from disk | Use HSM or strong password + encrypted storage |
Principles
This skill embodies the following CODITECT principles:
#2 First Principles Thinking:
- Understand WHY we sign: prove authenticity, ensure integrity, establish trust
- Apply cryptographic best practices (SHA256+, trusted CAs)
#5 Eliminate Ambiguity:
- Clear platform-specific instructions (macOS vs Windows vs Linux)
- Explicit verification commands for each platform
#8 No Assumptions:
- Verify signature immediately after signing (don't assume success)
- Test signed binary on clean machine (not just developer machine)
- Confirm timestamp server reachable before signing
#9 Security by Design:
- Certificates stored in secure secrets management (not git)
- Use hardware security modules (HSM) for production certificates
- Audit log of all signing operations
#10 Automation First:
- CI/CD integration for automated signing
- No manual signing steps (eliminates human error)
- Automated verification in pipeline
Full Standard: CODITECT-STANDARD-AUTOMATION.md
Additional Resources
- MACOS.md: Detailed macOS signing guide
- WINDOWS.md: Detailed Windows signing guide
- LINUX.md: Detailed Linux GPG guide