Skip to main content

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

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. 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

PlatformToolSign CommandVerify Command
macOScodesigncodesign --sign "Dev ID" binarycodesign --verify binary
WindowsSignToolsigntool sign /sha1 THUMB binary.exesigntool verify /pa binary.exe
LinuxGPGgpg --detach-sign binarygpg --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:

ProviderURLProtocol
DigiCerthttp://timestamp.digicert.comRFC 3161
Sectigohttp://timestamp.sectigo.comRFC 3161
GlobalSignhttp://timestamp.globalsign.com/tsa/r6advanced1RFC 3161
Comodohttp://timestamp.comodoca.comAuthenticode

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-PatternProblemSolution
No timestampSignature expires with certificateAlways include timestamp from trusted server
Hardcoded credentialsCertificate/password in code repositoryUse CI/CD secrets, never commit credentials
Self-signed certificatesUsers see "Unknown publisher" warningsUse certificate from trusted CA
SHA1 signingDeprecated, insecure (collision attacks)Use SHA256 or SHA512 minimum
No certificate rotationExpired certificate stops distributionMonitor expiration, renew 30 days early
Signing after uploadBinary modified between sign and distributeSign immediately before distribution
Missing verificationBroken signatures go unnoticedVerify signature immediately after signing
No backup of certificateLost certificate prevents urgent releasesSecurely backup certificate and password
Manual signing onlyBottleneck for releases, error-proneAutomate signing in CI/CD pipeline
Weak password protectionCertificate stolen from diskUse 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