Sigstore (Keyless)
In this tutorial you’ll sign a component version with Sigstore and verify it again — without generating a key pair. Your OIDC identity (Google, GitHub, or Microsoft) is what proves authorship, and a verifier only needs to know which identity to trust.
For the conceptual background — what Fulcio, Rekor, and TUF each do, and how identity-based trust differs from key pinning — see Concept: Sigstore (Keyless) and Concept: Identity-Based Trust.
What You’ll Learn
- Configure OCM to use your OIDC identity as a signing credential
- Sign a component version with
ocm sign cvagainst public Sigstore - Read the recorded identity out of a Sigstore signature
- Verify the signature by declaring whose identity you trust
Estimated time: ~10 minutes
How It Works
flowchart LR
subgraph sign ["Sign (You)"]
direction TB
A[Component Version] --> B[Browser-based login]
B --> IDP[("OIDC IdP<br/>Google / GitHub / Microsoft")]
IDP --> DEX[("Dex<br/>oauth2.sigstore.dev<br/>federation gateway")]
DEX --> FULCIO[("Fulcio<br/>issues short-lived cert<br/>(~10 min)")]
FULCIO --> SIGN[Sign descriptor digest]
SIGN --> REKOR[("Rekor<br/>transparency log entry")]
REKOR --> E[Signed Component Version<br/>+ bundle in signature.value]
end
E --> T["Share Component"]
T --> verify
subgraph verify ["Verify (Consumer)"]
direction TB
F[Signed Component Version] --> TUF[("TUF<br/>discover trust roots")]
TUF --> CERT[Validate Fulcio cert]
CERT --> PROOF[Verify Rekor inclusion proof]
PROOF --> H{Identity matches<br/>verifier spec?}
H -->|Yes| VALID["✓ Trusted"]
H -->|No| INVALID["✗ Rejected"]
end
style VALID fill:#dcfce7,color:#166534
style INVALID fill:#fee2e2,color:#991b1b
On the sign side: you log in at your OIDC provider, Dex relays the federated token to Fulcio, Fulcio issues a short-lived certificate bound to your identity, OCM signs the descriptor digest with the ephemeral key, and Rekor records the entry in its transparency log. The signature, certificate, and inclusion proof are bundled into the component descriptor.
On the verify side: TUF supplies the current trust roots, OCM validates the Fulcio certificate, checks the Rekor inclusion proof, and matches the certificate’s identity against the verifier spec. The consumer doesn’t need a public key — they declare which OIDC identity they trust, and OCM checks that the signature was made by that identity.
Prerequisites
- OCM CLI installed
- A web browser on the same machine — signing opens a browser window for OIDC login
- An account at one of {Google, GitHub, Microsoft} — public Sigstore federates with these
- Network access to
*.sigstore.dev— corporate firewalls sometimes block these - A component version to sign (we’ll create one if you don’t have one)
- Optional:
yqandjqfor inspecting the signature bundle (not required for signing or verification)
The OCM CLI invokes the cosign binary under the hood. If it’s not on your PATH (or the version is too old), OCM downloads and caches it under ~/.cache/ocm/cosign/... on first use. Subsequent runs skip the download.
Scenario
- Component:
github.com/acme.org/helloworld:1.0.0in a local CTF archive - Working directory:
/tmp/ocm-sigstore-tutorial - Signer identity: your OIDC login at Google, GitHub, or Microsoft
Steps
Create a sample component (if needed)
If you already have a component version in a CTF archive, e.g. by following our Create a Component Version guide, skip to the next step.
Otherwise create a small helloworld component:
mkdir -p /tmp/ocm-sigstore-tutorial && cd /tmp/ocm-sigstore-tutorial cat > component-constructor.yaml << 'EOF' components: - name: github.com/acme.org/helloworld version: 1.0.0 provider: name: acme.org EOF ocm add cvExpected output
COMPONENT │ VERSION │ PROVIDER ────────────────────────────────┼─────────┼────────── github.com/acme.org/helloworld │ 1.0.0 │ acme.orgThis creates a
transport-archivedirectory containing your component version.Configure the signing credential
Sigstore’s signer credential is your OIDC identity, not a private key on disk. Tell OCM to obtain it interactively at sign time by adding a consumer entry to
.ocmconfig:cat > .ocmconfig << 'EOF' type: generic.config.ocm.software/v1 configurations: - type: credentials.config.ocm.software consumers: - identity: type: SigstoreSigner/v1alpha1 signature: default credentials: - type: OIDCIdentityTokenProvider/v1alpha1 EOFTwo things are happening here:
identity.type: SigstoreSigner/v1alpha1— whenocm signlooks up a credential, this is the consumer identity it asks for. Thesignature: defaultfield matches the default signature name; if you sign with--signature prod, you’d add a second entry withsignature: prod.credentials.type: OIDCIdentityTokenProvider/v1alpha1— instructs OCM to run the interactive OIDC flow (open a browser, exchange the code for a token) instead of reading a static token from the config. This is the only credential type that triggers the browser flow.
No
algorithmfield is needed in the consumer identity — Sigstore is the only signing algorithm that uses this consumer type, so the lookup is unambiguous.Create the signer spec
The signer spec selects which signing handler runs. Create
sigstore-sign.yaml:cat > sigstore-sign.yaml << 'EOF' type: SigstoreSigningConfiguration/v1alpha1 EOFThat’s the full signer spec for public Sigstore. With no other fields, the handler uses the public-good Fulcio (
fulcio.sigstore.dev) and Rekor (rekor.sigstore.dev) endpoints, with trust roots discovered automatically via TUF.Spec and credential work as a pairThe signer spec picks how to sign (which handler, which endpoints). The
.ocmconfigconsumer identity provides the credential the handler asks for (the OIDC token, in this case). Both must be present for signing to succeed — the spec on its own has no token, the credential on its own has no handler. Linking them is the consumer-identitytypeandsignaturefields.Sign the component version
Run the sign command with both files:
ocm sign cv \ --config ./.ocmconfig \ --signer-spec ./sigstore-sign.yaml \ ./transport-archive//github.com/acme.org/helloworld:1.0.0A browser window opens against the public Sigstore login page (Dex). Pick your identity provider (Google, GitHub, or Microsoft), authenticate, and you’ll see an OCM “Signing identity verified!” page. Return to the terminal — signing continues automatically.
What just happened, step by step:
- OCM exchanged your OIDC token at Fulcio for a short-lived signing certificate (~10 minutes validity) bound to your email address.
- It hashed the component descriptor and signed the hash with the certificate’s ephemeral key.
- The signed entry was recorded in the Rekor transparency log.
- The signature, the Fulcio certificate, and the Rekor inclusion proof are bundled into the component descriptor’s
signaturesfield as one self-contained blob.
Expected output
digest: hashAlgorithm: SHA-256 normalisationAlgorithm: jsonNormalisation/v4alpha1 value: 4e376182b3d535143e8e009b1e467df3a5b0c1f912c71ae432200654c355606f name: default signature: algorithm: sigstore mediaType: application/vnd.dev.sigstore.bundle.v0.3+json value: eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZGV2LnNpZ3N0b3JlLmJ1bmRsZS52MC4z... time=2026-05-20T15:32:55.725+02:00 level=INFO msg="signed successfully" name=default digest=4e376182b3d535143e8e009b1e467df3a5b0c1f912c71ae432200654c355606f hashAlgorithm=SHA-256 normalisationAlgorithm=jsonNormalisation/v4alpha1Inspect the signature
Before verifying, take a look at what was actually written to the component descriptor. This is where Sigstore’s identity-based trust becomes concrete:
ocm get cv ./transport-archive//github.com/acme.org/helloworld:1.0.0 -o yaml \ | yq '.[0].signatures[] | select(.signature.algorithm == "sigstore")'You should see your signature with the recorded identity:
name: default digest: hashAlgorithm: SHA-256 normalisationAlgorithm: jsonNormalisation/v4alpha1 value: 4e376182b3d535143e8e009b1e467df3a5b0c1f912c71ae432200654c355606f signature: algorithm: sigstore mediaType: application/vnd.dev.sigstore.bundle.v0.3+json value: <base64-encoded Sigstore bundle>The
valuefield is the full Sigstore bundle — base64-encoded JSON containing three things in one self-contained blob: the signature bytes, the Fulcio certificate (your email is recorded as the certificate’s Subject Alternative Name, and the OIDC issuer URL is recorded as a certificate extension), and the Rekor inclusion proof. All three travel with the component descriptor; nothing needs to be fetched at verify time.The exact bundle layout is defined by the Sigstore protobuf bundle spec — see the upstream Sigstore documentation for the full schema. To inspect a specific bundle, decode the
valuefield withbase64 -dand pipe it throughjq, e.g. by appending| yq '.signature.value' | base64 -d | jqto the previous command.Configure the verifier identity
Verification is “do I trust this identity?” — not “do I have this public key?” Tell OCM which identity you trust by writing a verifier spec.
Two values matter:
certificateIdentity— the email or workload identity of whoever signed (e.g. your own email, copied from the previous step’s output). UsecertificateIdentityRegexpinstead to match a pattern of identities (see below).certificateOIDCIssuer— which OIDC provider they logged in with (different providers can have the same email). UsecertificateOIDCIssuerRegexpinstead to match a pattern of issuers.
You must set one identity field and one issuer field — exact or regex.
Create
sigstore-verify.yaml:cat > sigstore-verify.yaml << 'EOF' type: SigstoreVerificationConfiguration/v1alpha1 certificateOIDCIssuer: https://accounts.google.com certificateIdentity: jane.doe@example.com EOFReplace
certificateIdentitywith the email you logged in with, and adjustcertificateOIDCIssuerto match your provider:Signer logged in via certificateOIDCIssuervalueGoogle https://accounts.google.comGitHub https://github.com/login/oauthMicrosoft https://login.microsoftonline.comcertificateOIDCIssueris the upstream IdP, not Sigstore DexPublic Sigstore uses Dex (
oauth2.sigstore.dev) as a federation gateway, but the issuer URL recorded in the Fulcio certificate is the upstream identity provider — the one in the table above. Don’t putoauth2.sigstore.devhere.Two unrelated “issuer” concepts
The verifier’s
certificateOIDCIssuerfield is not the same thing as the OCM signatureissuerfield used by the RSA/PEM signing handlers. They are independent concepts that happen to share a name:- OCM signature issuer — a descriptor-level field on the signature, carrying an RFC 2253 Distinguished Name (e.g.
CN=Signer,O=Acme,C=US). It is set on the signing side by the RSA/PEM handlers and inspected by their verifiers. Sigstore signatures don’t use it. - Sigstore OIDC issuer — a URL recorded as an extension on the Fulcio certificate inside the Sigstore bundle, identifying which OIDC provider authenticated the signer. The verifier spec’s
certificateOIDCIssueris matched against this extension.
When you write a verifier spec for Sigstore, you are configuring the Sigstore OIDC issuer check only.
Trust a pattern of identities, not a single email
certificateIdentityandcertificateOIDCIssuerrequire an exact match. For team-wide trust — “anyone at my org who logged in via our IdP” — use the regex variantscertificateIdentityRegexpandcertificateOIDCIssuerRegexpinstead. Realistic example: trust any signer with an@example.comemail who logged in via Google:type: SigstoreVerificationConfiguration/v1alpha1 certificateOIDCIssuer: https://accounts.google.com certificateIdentityRegexp: ^[^@]+@example\.com$The exact and regex variants are mutually exclusive per field — set
certificateIdentityorcertificateIdentityRegexp, not both. The same applies tocertificateOIDCIssuer/certificateOIDCIssuerRegexp. Anchor your patterns (^...$) and escape literal dots (\.) — an unanchored.*@example\.comwould also matchattacker@example.com.evil.io.Verify the signature
Run verify with the verifier spec:
ocm verify cv \ --verifier-spec ./sigstore-verify.yaml \ ./transport-archive//github.com/acme.org/helloworld:1.0.0Expected output
time=2026-05-20T15:35:18.412+02:00 level=INFO msg="verifying signature" name=default time=2026-05-20T15:35:18.951+02:00 level=INFO msg="signature verification completed" name=default duration=539.512209ms time=2026-05-20T15:35:18.951+02:00 level=INFO msg="SIGNATURE VERIFICATION SUCCESSFUL"✅ Success! ✅ The component version is verified as authentic and signed by the identity you trusted.
What just ran: OCM extracted the Sigstore bundle from the descriptor, validated the Fulcio certificate against TUF-discovered trust roots, checked the Rekor inclusion proof, and finally compared the certificate’s identity against your verifier spec. None of those steps required a public key from the signer — the certificate-bound identity is the trust anchor.
What You’ve Learned
- ✅ Configured OCM to use your OIDC identity as a signing credential, with no key pair to manage
- ✅ Signed a component version using a short-lived Fulcio certificate
- ✅ Read the recorded identity out of a Sigstore signature
- ✅ Verified the signature by declaring which identity you trust
- ✅ Saw how spec files and
.ocmconfigconsumer identities link viasignaturename
Where to next
- Running an enterprise Sigstore stack? How-to: Sign Component Versions covers the
signingConfigfield and thetrusted_root_jsoncredential for private deployments. - Curious about the theory? Concept: Signing and Verification explains identity-based trust, how it differs from RSA key pinning, and why Sigstore works in air-gapped scenarios.
- Other algorithms? Tutorial: Plain Signatures (RSA key pair) and Tutorial: Certificate Chains (PEM) (PKI-based).
Cleanup
rm -rf /tmp/ocm-sigstore-tutorialRelated Documentation
- Concept: Signing and Verification — Identity-based trust and the Sigstore stack
- How-to: Sign Component Versions — Task-oriented sign reference (RSA and Sigstore)
- How-to: Verify Component Versions — Task-oriented verify reference
- ADR 0017: Sigstore Integration — Sigstore design and OIDC flow details