QCecuring - Enterprise Security Solutions

Code Signing and Software Supply Chain Security: A Complete Guide

Code signing 20 Mar, 2026 · 05 Mins read

Code signing proves software authenticity and integrity. Here's how to implement it across CI/CD pipelines, protect signing keys, and defend against supply chain attacks like SolarWinds and xz-utils.


SolarWinds (2020): attackers compromised the build system and injected malware into a signed software update. 18,000 organizations installed it — because it was signed by SolarWinds’ legitimate code signing certificate.

xz-utils (2024): a social engineering campaign over 2 years inserted a backdoor into a critical Linux compression library. It passed code review, was merged, and would have been distributed to virtually every Linux system.

Codecov (2021): attackers modified a bash script in Codecov’s CI pipeline. Every company using Codecov’s CI integration leaked their environment variables (including secrets) for 2 months.

The common thread: supply chain attacks bypass all endpoint security because the malicious code arrives through trusted channels — signed, reviewed, and distributed by legitimate infrastructure.

Code signing is the first line of defense. But signing alone isn’t enough. You need signing + provenance + verification + monitoring across the entire supply chain.


What Code Signing Actually Protects Against

Code signing provides two guarantees:

1. Authenticity: This software was published by the claimed entity (verified by the CA that issued the signing certificate).

2. Integrity: This software hasn’t been modified since it was signed (any change invalidates the signature).

What code signing does NOT protect against:

  • Malicious code signed by a legitimate key (SolarWinds scenario — the build was compromised before signing)
  • Compromised signing keys (attacker signs their own malware)
  • Dependencies with vulnerabilities (signing doesn’t audit code quality)

This is why code signing is necessary but not sufficient. You also need: build provenance, dependency verification, and runtime verification.


The Modern Code Signing Stack

Layer 1: Sign Everything

Every artifact that leaves your build pipeline should be signed:

Artifact TypeSigning MethodTool
Windows executables (.exe, .dll)Authenticodesigntool, AzureSignTool
macOS applications (.app)Apple codesigncodesign + notarytool
Java archives (.jar, .war)JAR signingjarsigner
Linux packages (.rpm, .deb)GPG signingrpm —addsign, dpkg-sig
Container imagesOCI signaturescosign (Sigstore)
Helm chartsProvenancehelm package —sign
npm packagesPackage signaturesnpm publish (with provenance)
Python packagesPGP/Sigstoretwine upload (with attestation)
Go binariesSigstorego build + cosign
FirmwareSecure Boot signingsbsign, UEFI signing tools

Layer 2: Protect the Signing Key

The signing key is the most valuable asset in your supply chain. If compromised, an attacker can sign anything as you.

Key storage hierarchy:

Best:  HSM (FIPS 140-2 Level 3) — key never extractable
Good:  Cloud KMS (AWS KMS, Azure Key Vault, GCP KMS) — key never exposed via API
OK:    Secrets manager (Vault) — key delivered to signing process at runtime
Bad:   CI/CD secret (environment variable) — extractable by any pipeline job
Worst: Developer's laptop — one theft away from compromise

Since June 2023, the CA/Browser Forum requires all code signing private keys to be stored in hardware (HSM, hardware token, or cloud HSM). Software-stored code signing keys are no longer permitted for publicly-trusted certificates.

Layer 3: Build Provenance (SLSA)

Signing proves WHO signed. Provenance proves HOW it was built:

{
  "subject": {
    "name": "myapp-v2.1.0.tar.gz",
    "digest": {"sha256": "abc123..."}
  },
  "predicate": {
    "buildType": "https://github.com/slsa-framework/slsa-github-generator",
    "builder": {"id": "https://github.com/actions/runner"},
    "invocation": {
      "configSource": {
        "uri": "git+https://github.com/myorg/myapp@refs/tags/v2.1.0",
        "digest": {"sha1": "def456..."}
      }
    }
  }
}

This provenance attestation (SLSA Level 3) proves:

  • The artifact was built from this specific Git commit
  • On this specific CI/CD platform
  • Using this specific workflow file
  • No human could have modified the build process

Layer 4: Verify Before Deploy

Signing is useless if nobody checks the signature. Verification must be enforced:

Kubernetes (Kyverno policy):

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  rules:
  - name: verify-cosign-signature
    match:
      resources:
        kinds: ["Pod"]
    verifyImages:
    - imageReferences: ["ghcr.io/myorg/*"]
      attestors:
      - entries:
        - keyless:
            issuer: "https://token.actions.githubusercontent.com"
            subject: "https://github.com/myorg/*"

Windows (AppLocker/WDAC):

# Only allow executables signed by specific publishers
New-CIPolicy -Level Publisher -FilePath policy.xml
# Blocks unsigned or unknown-signed executables from running

macOS (Gatekeeper):

# Verify signature before running
codesign --verify --deep --strict MyApp.app
spctl --assess --type execute MyApp.app

CI/CD Pipeline Integration

GitHub Actions (Keyless Signing with Sigstore)

name: Build and Sign
on:
  push:
    tags: ['v*']

jobs:
  build-sign:
    runs-on: ubuntu-latest
    permissions:
      id-token: write      # For OIDC token (keyless signing)
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Build container image
        run: docker build -t ghcr.io/myorg/myapp:${{ github.ref_name }} .

      - name: Push to registry
        run: docker push ghcr.io/myorg/myapp:${{ github.ref_name }}

      - name: Sign with cosign (keyless)
        uses: sigstore/cosign-installer@v3
      - run: |
          cosign sign ghcr.io/myorg/myapp@$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/myorg/myapp:${{ github.ref_name }} | cut -d@ -f2)

      - name: Generate SLSA provenance
        uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0

Azure DevOps (HSM-backed Signing)

steps:
  - task: DotNetCoreCLI@2
    inputs:
      command: 'publish'
      publishWebProjects: true

  - script: |
      AzureSignTool sign \
        -kvu "$(SigningVaultURL)" \
        -kvi "$(SigningClientId)" \
        -kvs "$(SigningClientSecret)" \
        -kvc "$(CertificateName)" \
        -tr http://timestamp.digicert.com \
        -td sha256 \
        "$(Build.ArtifactStagingDirectory)/**/*.exe"
    displayName: 'Sign executables with Azure Key Vault'

GitLab CI (Vault-backed Signing)

sign:
  stage: sign
  script:
    - export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=signer jwt=$CI_JOB_JWT)
    - vault write -field=signature transit/sign/code-signing-key \
        input=$(base64 < build/myapp) > signature.b64
    - cosign attach signature --signature $(cat signature.b64) \
        registry.example.com/myapp:$CI_COMMIT_TAG

Defending Against Supply Chain Attacks

Attack: Compromised Build System (SolarWinds Pattern)

How it works: Attacker gains access to CI/CD, modifies the build to inject malware, legitimate signing key signs the compromised build.

Defenses:

  • SLSA Level 3+ provenance: Proves the build came from a specific source commit on a hardened builder. If the build was tampered with, provenance won’t match.
  • Reproducible builds: Anyone can rebuild from source and verify the output matches the signed artifact. Injected code would produce a different hash.
  • Separated signing: Build system produces unsigned artifacts. A separate, hardened signing system signs them after verification. Compromising the build system doesn’t grant signing access.

Attack: Dependency Confusion

How it works: Attacker publishes a malicious package to a public registry with the same name as your internal package. Build system fetches the public (malicious) version.

Defenses:

  • Lock files with hashes: package-lock.json, go.sum, Pipfile.lock pin exact versions with integrity hashes. Any change is detected.
  • Private registry priority: Configure package managers to check your private registry first.
  • Scoped packages: Use @myorg/package-name (npm) or organization-scoped names that can’t be squatted on public registries.

Attack: Compromised Signing Key

How it works: Attacker steals the code signing private key and signs malware.

Defenses:

  • HSM storage: Key is non-extractable. Attacker must compromise the signing infrastructure, not just steal a file.
  • Signing approval gates: Require human approval (or automated policy check) before each signing operation.
  • Signing volume monitoring: Alert on unusual signing patterns (signing at 3 AM, signing 100 artifacts when normal is 5/day).
  • Certificate Transparency for code signing: Sigstore’s Rekor log records all signing events publicly. Unauthorized signatures are detectable.

Timestamp: The Often-Forgotten Requirement

Code signing without a timestamp has a critical flaw: when the signing certificate expires, the signature becomes unverifiable. Software signed 2 years ago with a now-expired certificate shows “signature expired” warnings.

Timestamping proves WHEN the signature was created. If the certificate was valid at signing time (proven by the timestamp), the signature remains valid forever — even after the certificate expires.

# Always include a timestamp
signtool sign /fd SHA256 \
  /tr http://timestamp.digicert.com \  # RFC 3161 timestamp
  /td SHA256 \
  MyApp.exe

# Without /tr: signature expires with certificate (1-3 years)
# With /tr: signature valid indefinitely

Never skip timestamping. There’s no reason to — timestamp services are free and add milliseconds to the signing process.


FAQ

Q: Do I need code signing if I only deploy to my own infrastructure? A: Yes — code signing proves that what’s running in production is what your CI/CD built. Without it, a compromised deployment pipeline or container registry can inject modified artifacts that look legitimate.

Q: Sigstore (keyless) vs traditional code signing — which should I use? A: Sigstore for container images and internal artifacts (simpler, no key management). Traditional code signing (with HSM-stored key) for software distributed to customers (Windows executables, macOS apps, Java JARs) — because OS verification requires traditional certificates.

Q: How do I handle signing for open-source projects? A: Use Sigstore (free, keyless, identity-based). Sign with your GitHub identity. Consumers verify the signature came from the expected GitHub Actions workflow. No signing keys to manage or protect.

Q: What happens if my signing key is compromised? A: Revoke the certificate immediately (contact your CA). All software signed after the compromise date is suspect. Software signed before (with valid timestamps) remains trustworthy. Notify customers. Generate a new key. Re-sign current releases with the new key.

Stay Ahead on Crypto & PKI

Monthly insights on certificate management, post-quantum readiness, and enterprise security.

Subscribe Free

Related Insights

SSL/TLS

OpenSSL Complete Guide: Commands, Configuration & Troubleshooting

Master OpenSSL with this comprehensive guide covering certificate generation, CSR creation, chain verification, TLS debugging, format conversion, and production hardening. Every command you'll ever need.

By Shivam sharma

10 May, 2026 · 08 Mins read

SSL/TLSPractical GuidesDevOps

Clm

Certificate Outages: The $500K Problem Nobody Budgets For

Expired certificates cause more outages than cyberattacks. Here's the real cost of certificate outages, why they keep happening, and the engineering practices that eliminate them.

By Shivam sharma

05 May, 2026 · 05 Mins read

ClmSecurityEnterprise

CLM

How to Automate Certificate Renewal with ACME Protocol: A Practical Guide

ACME automates TLS certificate issuance and renewal without human intervention. Here's how to set it up with Certbot, acme.sh, and cert-manager — with real configs for Nginx, Apache, and Kubernetes.

By Ayush kumar rai

03 May, 2026 · 06 Mins read

CLMDevOpsPKI

Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.