Technical Guide Advanced 13 min read

Sigstore: Keyless Signing for Software Supply Chains

Sigstore is an OpenSSF project that lets developers and CI pipelines sign software artifacts without managing long-lived private keys. It replaces signing keys hidden in HSMs and CI vaults with OIDC-based identity and short-lived certificates. This guide covers Fulcio, Rekor, Cosign, Gitsign, and how to run keyless signing in production.

Quick Facts

Type
Technical Guide
Level
Advanced
Next
Certificate Transparency

Overview

Sigstore is an OpenSSF project that lets developers and CI pipelines sign artifacts without managing long-lived private keys. It is made of a small set of components: Fulcio (the certificate authority), Rekor (the transparency log), Cosign (the CLI used to sign containers, blobs, and attestations), and Gitsign (for Git commits). All four are designed to be operated as a service, either via the Sigstore public instance or as a private deployment.

The trick is identity. Instead of binding a signature to a key that the user owns and protects for years, Sigstore binds it to an OIDC identity — a GitHub Actions workflow, a Google account, a corporate IdP — and issues an X.509 certificate valid for roughly ten minutes. The ephemeral private key never has to be stored, rotated, or revoked, because by the time anyone could attempt to misuse it, the certificate is already expired.

What is verified later, then, is no longer "did you protect your key?" but "did the right identity sign this, at a time the log can prove?". This guide walks through how the pieces fit together, what the verifier actually checks, where Sigstore changes the operational model compared to traditional code signing certificates, and how it borrows the auditability model of Certificate Transparency.

The four moving parts

For two decades, code signing has rested on the same assumption: a developer or a release engineer guards a private key, sometimes inside a hardware security module, sometimes inside a CI vault, and the world trusts whatever that key signs. The model works, but it ages badly. Keys leak from CI runners, get smuggled out of laptops, get embedded in container images by accident, and live for years past the moment anyone could plausibly notice that they were stolen. The Solorigate compromise of SolarWinds, the npm package hijacks, the leaked Nvidia code signing certificates abused by malware — all of them share a common shape: a long-lived key, in a place it should not have been, signing things it should not have signed.

Sigstore takes a different approach. Instead of asking "how do we protect the key better?", it removes the long-lived key from the picture entirely. A new key pair is generated for every signature, used once, and then discarded. The certificate that vouches for that ephemeral key is valid for only about ten minutes, which is enough time to sign an artifact but not enough time to do anything else with it. What ties the signature back to a real identity is not the key — it is an OIDC token issued by an identity provider the verifier already trusts.

The project sits inside the OpenSSF ecosystem and is operated both as a public good (sigstore.dev runs a free, publicly auditable instance) and as a model that any organisation can re-host privately. Its four moving parts work together in a sequence that is worth walking through end to end.

The important shift is not in what is signed, but in what is trusted. In traditional code signing, the verifier trusts a key holder. In Sigstore, the verifier trusts an identity provider and a log operator. Neither of those is automatically better — they are simply different surfaces of attack, and for CI-built artifacts the new surface is usually the right trade.

1

Authenticate via OIDC

The signer — a developer at a terminal or a CI workload such as a GitHub Actions job — obtains an OIDC ID token from a supported provider (GitHub OIDC, Google, Microsoft, a corporate IdP federated through Dex, and so on). For machine workflows this happens ambiently, with no human interaction.

2

Request a certificate from Fulcio

The signer generates an ephemeral key pair locally, then sends a certificate signing request to Fulcio along with the OIDC token. Fulcio verifies the token's signature against the provider's JWKS, extracts the identity claims, and issues an X.509 certificate that binds the public key to that identity as a URI in the Subject Alternative Name (for example `https://github.com/org/repo/.github/workflows/release.yml@refs/tags/v1.2.0`). The certificate is valid for roughly ten minutes.

3

Sign the artifact with Cosign

Cosign computes the digest of the artifact (a container image, a tarball, an in-toto attestation), signs the digest with the ephemeral private key, and stops. The private key is held in memory only and is destroyed when the process exits.

4

Publish to Rekor

The signature, the Fulcio certificate, and a small piece of metadata are appended as an entry to Rekor, an immutable, publicly verifiable Merkle-tree transparency log. Rekor returns a signed inclusion proof and a timestamp. That timestamp is what later anchors trust in the signature, because by the time a verifier looks at it, the certificate's own notBefore/notAfter window will have closed.

5

Verifier checks all four

A consumer of the artifact later walks the same chain in reverse: the signature is valid for the digest, the certificate chains to a known Fulcio root, the identity in the SAN matches the policy ("only the release workflow on this repo, tagged with a semver tag, is allowed to sign"), and the Rekor inclusion proof shows the entry was logged while the certificate was still valid.

The components

There is also a fifth piece worth mentioning: the TUF (The Update Framework) root that distributes Sigstore's trust roots themselves. Verifiers do not hard-code Fulcio's root certificate or Rekor's public key; they fetch them via a signed TUF metadata channel, which lets the project rotate roots safely. This is the kind of operational detail that matters only the day something breaks, but it is worth knowing it exists before that day.

Fulcio

The keyless certificate authority. Fulcio takes an OIDC token and a CSR, validates the token's signature and audience claim, and issues a short-lived X.509 certificate (~10 minutes) whose Subject Alternative Name encodes the verified identity as a URI. The Fulcio root runs from an HSM-backed key and is publicly auditable: the Fulcio root is distributed via TUF metadata, and every Fulcio-issued certificate is recorded in a public CT log for monitoring.

Rekor

The transparency log. Rekor stores signed entries in an append-only Merkle tree, in the same spirit as Certificate Transparency for the WebPKI. Anyone can fetch the log, replay it, and verify its consistency. Every Rekor entry is timestamped by the log itself, and the inclusion proof returned at signing time is what lets a verifier later assert "this was signed while the cert was live".

Cosign

The CLI most users interact with. It signs container images by digest, arbitrary blobs, in-toto attestations, and SBOMs. It also verifies the full Fulcio + Rekor proof chain, with strict policies on the identity that signed and the issuer that authenticated it.

Gitsign

The Git commit signing tool. It plugs into `git commit -S` as a credential helper, performs the same OIDC-to-Fulcio-to-Rekor dance, and replaces the long-lived GPG keys that most repositories never managed to rotate.

Sigstore vs traditional code signing

The clearest way to see what Sigstore changes is to put the two models side by side and look at how each property shifts.

Neither column is universally better. Traditional code signing wins anywhere the consumer must verify the signature offline, years after issuance, with no ability to reach a log — operating system bootloaders, firmware updates, kiosk devices. Sigstore wins anywhere the consumer is online, the signing event is frequent, and the identity binding matters more than the long-term key.

Traditional code signingSigstore (keyless)
Key custodyLong-lived private key on developer laptop, build server, or HSMEphemeral key pair generated per signature, destroyed seconds later
Identity bindingTrust the human or organisation that holds the keyTrust the OIDC identity that was authenticated at signing time
Certificate lifetime1 to 3 years, often longerAbout 10 minutes
Compromise blast radiusEvery artifact signed until the key is rotated and revokedOne signing event
AuditabilityRevocation via CRL or OCSP, with no public record of issuancePublic, append-only Rekor log for every signature
RevocationRequired, complex, often slow to propagateLargely irrelevant — the certificate is already expired
Verifier complexityVerify signature, build chain, check CRL or OCSPVerify signature, chain to Fulcio root, validate Rekor inclusion proof, enforce identity policy
Best suited forReleased software, kernel modules, drivers, signed firmware, MSI installersCI-built artifacts, container images, SBOMs, in-toto attestations, Git commits

What signing and verifying actually look like

Notice that the image is referenced by digest, not by tag. Cosign refuses to sign a mutable tag because the whole point of the signature is to bind to an immutable content-addressable artifact.

The identity policy is the part operators get wrong most often. A verification that only checks "any Fulcio certificate" is functionally meaningless — anyone with a GitHub account can obtain one. The policy must pin the exact workflow path, branch or tag pattern, and issuer.

For attestations (SBOMs, SLSA provenance, vulnerability scans), the surface looks the same but uses `cosign verify-attestation`:

In a Kubernetes admission controller (for example with Sigstore's policy-controller or with Kyverno), the same checks run at every pod admission, blocking images whose signature, identity, or attestation does not satisfy the cluster's policy.

# Sign a container image with Cosign keyless (default behaviour in Cosign v2.x)
cosign sign ghcr.io/example/api@sha256:7f8a...

# What this actually does:
#   1. opens a browser tab to your OIDC provider, or uses ambient
#      GitHub OIDC if running inside a GitHub Actions job
#   2. exchanges the ID token with Fulcio for a ~10-minute X.509 cert
#   3. signs the image digest with an ephemeral key, attested by that cert
#   4. uploads signature + cert to the OCI registry as a separate tag
#   5. uploads the entry to Rekor and stores the inclusion proof alongside
# Verify keyless, pinning the expected identity and issuer
cosign verify ghcr.io/example/api@sha256:7f8a... \
--certificate-identity-regexp '^https://github\.com/example/api/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

# Verification fails closed if:
#   - the signature does not match the artifact digest
#   - the identity or issuer do not match the policy regex
#   - no valid Rekor inclusion proof exists
#   - the certificate did not chain to a trusted Fulcio root at the
#     time recorded by Rekor
# Verify a SLSA provenance attestation tied to the build
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp '^https://github\.com/example/api/\.github/workflows/release\.yml@refs/tags/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/example/api@sha256:7f8a...
Keyless does not mean "no cryptography". A key pair is still generated for every signature — the project name refers to the fact that no human or machine is ever responsible for storing a private key across signing events. The compromise model shifts from "did anyone steal my key?" to "did anyone trick my OIDC provider into issuing a token for an identity I didn't authorise?".

Production considerations

Pin the identity, not just the certificate

A Cosign verification that omits `--certificate-identity[-regexp]` and `--certificate-oidc-issuer` accepts any signature produced by any Sigstore user. The verification must pin both the issuer URL and the identity pattern, ideally down to the specific workflow file, branch or tag pattern, and (for OIDC providers that expose them) the audience and event type. Treat the verification policy as code, reviewed and versioned alongside the rest of your supply-chain configuration.

Rekor is public

Every signature made against the public Sigstore instance is logged in Rekor — the workflow path, the repository name, the email of the developer who signed locally. That data is visible forever to anyone who can pull the log. For internal projects, unannounced products, or anything whose mere existence is sensitive, the public instance is not appropriate. The same software can be run as a private deployment where Fulcio, Rekor, and the TUF root are operated inside your own boundary.

Time-of-signing trust

Because the certificate is already expired by the time anyone verifies it, the verifier rebuilds trust by combining two timestamps: the Rekor entry time and the certificate's notBefore/notAfter window. If the Rekor entry was recorded while the certificate was valid, the signature is accepted. This means Rekor's integrity is load-bearing — if the log can be rewritten or its timestamps forged, the whole verification falls apart. The public log is audited externally; a private log is only as trustworthy as its operator. Plan for independent witnesses, off-site replicas, and periodic consistency proofs.

Not a replacement for everything

Sigstore is built for online, frequent, identity-driven signing. It does not replace long-lived release signing where the verifier is offline, the artifact lives for years, and the chain of trust must be checkable without network access — Windows kernel drivers, UEFI firmware, MSI installers distributed by physical media. For those, the right tool is still a long-lived code signing certificate bound to an HSM. The right architecture in most organisations is both paths, side by side, with clear policy on which path each artifact takes.

Verification is the hard part

Signing with Cosign is one command and a few lines of CI. Verification is where adoption stalls — and where the security value actually lives. Every consumption point matters: the Kubernetes admission controller pulling the image, the artifact registry serving it, the package manager installing it, the developer running it locally. If only some of those points verify, an attacker simply targets the others. Treat verification coverage as a measurable KPI, not a binary "we use Sigstore".

Inventory the short-lived too

Most certificate inventories were built for certificates that live for months and renew on a calendar. Sigstore produces certificates that live for minutes and never renew, in volumes that can reach millions per day on busy pipelines. A discovery and inventory approach that ignores them is blind to a meaningful fraction of the organisation's signing activity. The inventory should ingest Rekor (or the private log) and correlate it with the long-lived signing path.

Where Sigstore fits in a broader signing strategy

Most enterprises will not pick one model and abandon the other. The realistic picture looks like this:

- CI artifacts, container images, SBOM and SLSA attestations, Git commits: keyless via Sigstore, with identities pinned to specific workflows. This is high volume, frequent, online — exactly Sigstore's sweet spot. - Released binaries that ship to end users (desktop apps, agents, drivers, firmware): long-lived code signing certificates, HSM-bound, governed by a policy that constrains who can request them and how. - Internal services and machine identities: a mix of automated certificate management for TLS / mTLS and Sigstore for the artifacts that those services consume.

The interesting design question is not "Sigstore yes or no?" but "where does the boundary sit, and who enforces it consistently?". That is a CLM and policy question more than a Sigstore question.

How we help

Evertrust & Sigstore: Keyless Signing for Software Supply Chains

Private issuance for organisations that cannot use the public instancefor organisations whose workflow paths, repository names, or developer identities cannot be exposed to a public log, Evertrust PKI provides the policy-driven, audit-grade certificate issuance you need to back a privately operated signing infrastructure, keeping all metadata inside your own boundary.

Unified visibility across long-lived and short-lived certificatesEvertrust CLM keeps a single inventory across the long-lived code signing certificates that ship released binaries (kernel modules, installers, firmware) and the short-lived certificates produced by Sigstore. Two operational models, one source of truth.

Policy alignment across signing pathsreleases bound for end users go through the long-lived signing path with HSM-protected keys, CI artifacts go through keyless. Evertrust enforces that distinction with the same governance, approval, and audit model on both sides, so there are not two parallel security programmes.