Managed .NET client library for Sigstore bundle signing and verification. Uses System.Security.Cryptography + BouncyCastle for Ed25519 — no native binaries, no unsafe code. Targets .NET 8, 9, and 10.
Sigstore is an open-source project that makes software supply-chain signing and verification transparent, auditable, and accessible. Artifacts are signed with short-lived X.509 certificates issued by Fulcio — tied to an OIDC identity from GitHub Actions, Google, or Microsoft — and every signing event is recorded in the Rekor transparency log, eliminating the need to manage long-lived private keys.
| Feature | Status |
|---|---|
| Bundle verification — keyless (Fulcio + Rekor) | Supported |
| Bundle verification — managed public key | Supported |
| Bundle signing — keyless (OIDC + Fulcio + Rekor) | Supported |
Bundle formats — message_signature and DSSE |
Supported |
| In-toto attestation verification | Supported |
Digest-only verification (sha256:<hex>) |
Supported |
| Ed25519 signatures (via BouncyCastle) | Supported |
| ECDSA P-256 and RSA signatures | Supported |
| TUF trust bootstrap against the Public Good Instance | Supported |
| RFC 3161 timestamps and Rekor integrated time | Supported |
Dependency injection — AddSigstore() / AddSigstoreSigning() |
Supported |
| OIDC token providers — GitHub Actions, env var, ambient | Supported |
| Rekor2 bundles (TSA timestamps, Ed25519 checkpoints) | Supported |
| SCT validation and SET verification | Supported |
| Signing config — custom Fulcio/Rekor/TSA endpoints | Supported |
| Conformance test suite (3x matrix: net8/9/10) | 132 passed, 0 xfailed |
| OCI container signing via cosign | Interoperable (see Cosign Integration) |
dotnet add package Sigstore.Net
// Program.cs
builder.Services.AddSigstore();public class MyService(Verifier verifier)
{
public async Task VerifyAsync(string bundleJson, byte[] artifact)
{
VerificationPolicy policy = VerificationPolicy.ForGitHubActions(
repository: "my-org/my-repo",
gitRef: "refs/heads/main");
VerificationResult result = await verifier.VerifyAsync(
bundleJson, artifact, policy, CancellationToken.None);
Console.WriteLine($"Verified. Signed by: {result.Identity.Subject}");
}
}Verify bundles signed with a standalone public key (no Fulcio certificate):
string publicKeyPem = await File.ReadAllTextAsync("cosign.pub");
VerificationResult result = await verifier.VerifyWithKeyAsync(
bundleJson, artifact, publicKeyPem,
trustedRootJson: trustedRoot,
CancellationToken.None);using Microsoft.Extensions.Logging.Abstractions;
using Sigstore.Bundle;
using Sigstore.Crypto;
using Sigstore.Fulcio;
using Sigstore.Rekor;
using Sigstore.Time;
using Sigstore.Tuf;
using Sigstore.Verification;
using HttpClient http = new();
Verifier verifier = new(
new VerificationPipeline(
new BundleParser(),
new CertificateVerifier(),
new TransparencyLogVerifier(),
new SignatureVerifier(),
new DefaultSystemClock(),
NullLogger<VerificationPipeline>.Instance),
new TufClient(http, NullLogger<TufClient>.Instance),
NullLogger<Verifier>.Instance);
string bundleJson = await File.ReadAllTextAsync("artifact.sigstore.json");
byte[] artifact = await File.ReadAllBytesAsync("artifact.tar.gz");
VerificationPolicy policy = VerificationPolicy.ForExact(
issuer: "https://token.actions.githubusercontent.com",
subject: "repo:my-org/my-repo:ref:refs/heads/main");
VerificationResult result = await verifier.VerifyAsync(
bundleJson, artifact, policy, CancellationToken.None);builder.Services.AddSigstoreSigning(options =>
{
options.TokenProvider = new StaticTokenProvider(myOidcToken);
});public class MyService(Signer signer)
{
public async Task SignAsync(byte[] artifact)
{
SigningResult result = await signer.SignAsync(artifact, CancellationToken.None);
await File.WriteAllTextAsync("artifact.sigstore.json", result.BundleJson);
Console.WriteLine($"Signed by: {result.Identity.Subject}");
}
}| Provider | Source | Use case |
|---|---|---|
GitHubActionsTokenProvider |
GHA OIDC federation | CI/CD pipelines |
EnvVarTokenProvider |
SIGSTORE_ID_TOKEN env var |
Local dev, scripts |
AmbientTokenProvider |
Auto-detects GHA or env var | General purpose |
StaticTokenProvider |
Caller-provided token string | Tests, custom flows |
| Method | Matches | Typical use case |
|---|---|---|
VerificationPolicy.ForExact(issuer, subject) |
Exact OIDC issuer and subject string | Service account email, specific workflow ref |
VerificationPolicy.ForRegexSubject(issuer, pattern) |
Exact issuer, regex on subject | Wildcard across branches or repos |
VerificationPolicy.ForGitHubActions(repository, gitRef) |
GitHub Actions token for a specific repo + ref | CI/CD artifact provenance |
By default the library fetches trusted_root.json from the Sigstore TUF repository on the first call — the authoritative source for Fulcio CA certificates, Rekor public keys, and CT log information.
For air-gapped environments or tests, supply a trusted root directly:
string trustedRootJson = await File.ReadAllTextAsync("trusted_root.json");
VerificationResult result = await verifier.VerifyAsync(
bundleJson, artifact, policy,
trustedRootJson: trustedRootJson,
CancellationToken.None);// Verification only
services.AddSigstore();
// Verification + signing
services.AddSigstoreSigning(options =>
{
options.FulcioUrl = new Uri("https://fulcio.sigstore.dev/");
options.RekorUrl = new Uri("https://rekor.sigstore.dev/");
options.OidcAudience = "sigstore";
options.TokenProvider = new AmbientTokenProvider(httpClient);
options.HttpTimeout = TimeSpan.FromSeconds(30);
});sigstore-dotnet is tested against the official sigstore-conformance v0.0.25 test suite. Tests run as a 3x matrix across .NET 8, 9, and 10 on every push and weekly.
| Test category | Status |
|---|---|
Artifact verification — message_signature |
Pass |
| Artifact verification — DSSE envelope | Pass |
| In-toto attestation verification | Pass |
| Bundle v0.3 format | Pass |
| Managed-key verification | Pass |
| Ed25519 (rekor2) verification | Pass |
| Digest-only verification | Pass |
| Negative validation tests | Pass (all verification xfails cleared) |
| TSA timestamp validation | Pass (message imprint, cert chain, authority/cert validity) |
| SCT / CT log validation | Pass |
| SET (Signed Entry Timestamp) verification | Pass |
| canonicalizedBody cross-check | Pass (hashedrekord, dsse 0.0.1/0.0.2, intoto 0.0.2) |
| Multi-signer checkpoints | Pass (C2SP key hints, Ed25519 + ECDSA) |
Verified on every push against cosign and sigstore-python:
| Test | Direction |
|---|---|
| message_signature sign + verify | .NET ↔ Python (both directions) |
| DSSE in-toto attestation | Python → .NET |
| Digest-only verification | Both directions |
| cosign sign-blob → verify | cosign → .NET |
| .NET sign → cosign verify-blob | .NET → cosign |
| Tampered signature rejection | Both clients × both directions |
| Wrong issuer/identity rejection | Both clients × both directions |
Like sigstore-java and sigstore-python, this library focuses on artifact signing and verification. For container image signing, use cosign — the bundles are fully interoperable.
# Sign a container image with cosign, extract and verify the bundle with .NET
cosign sign --yes ghcr.io/my-org/my-image:latest
cosign save ghcr.io/my-org/my-image:latest --dir /tmp/image-bundle
# Verify the extracted bundle
dotnet-sigstore verify-bundle --bundle /tmp/image-bundle/bundle.sigstore.json ...
# Sign an artifact with .NET, attach to a container image with cosign
dotnet-sigstore sign-bundle --identity-token $TOKEN --bundle artifact.sigstore.json artifact.tar.gz
cosign attach signature --bundle artifact.sigstore.json ghcr.io/my-org/my-image:latestThe verification pipeline follows the Sigstore client specification:
- Parse bundle JSON (protobuf JSON encoding) — reject unknown media types
- Bootstrap trust via TUF — fetch and verify
trusted_root.json - Build and verify Fulcio certificate chain against trusted CAs (or resolve managed key); validate SCT log IDs against trusted CT logs
- Enforce identity policy (issuer + subject) — skipped for managed-key bundles
- Validate certificate validity window against Rekor integrated time and/or RFC 3161 timestamps; validate TSA certificate chains and authority validity windows
- Verify Rekor inclusion proof (Merkle path + signed checkpoint with Ed25519/ECDSA); cross-check canonicalizedBody (hashedrekord, dsse, intoto)
- Verify SET (signed entry timestamp) cryptographic signature
- Verify artifact signature (ECDSA, RSA, Ed25519) using the leaf certificate or provided public key
The signing pipeline generates an ephemeral ECDSA P-256 key, obtains a short-lived Fulcio certificate via OIDC, signs the artifact, uploads a transparency log entry to Rekor, and returns a Sigstore bundle v0.3 JSON.
See docs/architecture.md for a detailed walkthrough.
| Version | Scope |
|---|---|
| v0.1 | Bundle verification, TUF trust bootstrap, Fulcio chain, Rekor inclusion proof, RFC 3161 timestamps |
| v0.2 | Keyless signing pipeline, OIDC token providers, DI extensions |
| v0.3 | Ed25519 via BouncyCastle, managed-key verification, digest mode, in-toto attestations, negative validations |
| v0.4 | Conformance signing against real Sigstore infrastructure, Fulcio v2 REST API, canonicalizedBody cross-check, bundle v0.3 leaf-only cert |
| v0.5 | Full verification conformance — TSA validation, SCT/SET verification, multi-signer checkpoints, DSSE/intoto cross-checks (128 passed, 0 xfailed) |
| v0.6 | Signing conformance — signing-config, TSA timestamps, inclusion proof parsing, hashedrekord v0.0.2 |
| v0.7 | Full conformance — Rekor v2 API client, DER signatures, zero xfails (132 passed, 0 xfailed) |
| v0.8 | Cross-client interop tests — 12 test cases: message_signature, DSSE, digest, tamper detection, identity policy |
| v0.9 | Cosign interop — 15 tests including cosign sign/verify, documented OCI workflow via cosign |
| v1.0 (current) | Stable API, batch signing, staging preset, Codecov, DocFX site, full spec conformance |
Contributions are welcome — bug reports, features, documentation improvements, and conformance coverage all appreciated. Please read CONTRIBUTING.md before opening a pull request.
To report a security vulnerability, please use GitHub's private advisory system rather than opening a public issue. See SECURITY.md for the full policy.