sigstore/fulcio
repository·main·Indexed 21 days ago
https://github.com/sigstore/fulcioA free-to-use Certificate Authority (CA) that issues short-lived (10-minute) code signing certificates based on OpenID Connect (OIDC) identities. Fulcio provides an API accessible via HTTP and gRPC, publishes issued certificates to a Certificate Transparency (CT) log for auditability, and supports multiple CA backends including Google Cloud Private CA, Tink, PKCS#11, and Cloud KMS.
What's inside Fulcio
- Certificate Maker is a tool used to create X.509 certificates (root, intermediate, and leaf) that comply with Fulcio's certificate requirements. It generates certificates from JSON templates using Go standard library template parsing and JSON unmarshaling. The tool comes with embedded default templates, allowing it to function without requiring external template files.
What is Fulcio
mainFulcio is a free-to-use Certificate Authority (CA) designed for issuing short-lived code signing certificates. It issues certificates based on an OpenID Connect (OIDC) identity (such as an email address).
Key Characteristic: Fulcio only issues certificates that are valid for 10 minutes.
Identify Beta features in Fulcio
mainThe following features are currently in Beta and are subject to change according to the Sigstore API Stability Policy. Use these with caution in production environments:
- Fulcio API: The core API interface.
- Certificate Authority (CA) Support: Support for various backends including Google Private CA Service, PKCS11, and File-backed CA.
- Challenge Support: Support for SPIFFE challenges and OIDC-based email challenges.
- Go Client Library: The client library located in
fulcio/pkg. - Issuers: Configuration for issuers as defined in
fulcio-config.yaml.
How to pick a Subject Alternative Name (SAN)
mainSANs are used in verification policies as the primary identifier for a workload. When choosing a SAN for your service, consider these criteria:
- Policy Queryability: How will users want to write policies? A good test is whether the value works well with
cosign verify --certificate-identity=<VALUE>. - Specificity: Choose the most specific identifier that describes the workload without being too broad (granting unintended access) or too narrow (making management difficult).
- Stability: Avoid identifiers that change per instance (like UUIDs). If a policy requires a regex to match a SAN, the SAN is likely too specific.
- Uniqueness: SANs should be unique to the issuer and prevent one resource from spoofing another.
- Documentation: All SANs produced by a provider should be well-defined and documented.
Case Study: GitHub Actions
GitHub Actions uses
job_workflow_refas its SAN. This is preferred over other options because:- It is tied to a specific Job in a workflow.
- It supports reusable workflows, allowing centralized policies.
- It is more stable than a unique Job ID (which changes every run) and more specific than a Repository URL (which is too broad).
- Policy Queryability: How will users want to write policies? A good test is whether the value works well with
How Fulcio certificate issuance works
mainFulcio issues code signing certificates by verifying an OpenID Connect (OIDC) identity and proving possession of a private key. The process follows these steps:
- Certificate Request Input: The client submits an OIDC identity token (JWT), a public key, and a signed challenge (a signature of the
subclaim from the OIDC token). Alternatively, a Certificate Signing Request (CSR) can be provided. - Authentication: Fulcio authenticates the OIDC token by using the
iss(issuer) claim to find the issuer's discovery endpoint, downloading their signing keys, and verifying the token signature. - Verifying the Challenge: Fulcio verifies that the client owns the private key corresponding to the provided public key by validating the signed challenge or the CSR.
- Constructing the Certificate: Fulcio creates an X.509 certificate containing the client's public key, sets the Subject Alternative Name (SAN) to the OIDC
subclaim (e.g., email, SPIFFE ID, or GitHub Actions identity), and includes the OIDC issuer in a custom field. - Signing: The certificate is signed by a Certificate Authority (CA) backend to create a chain of trust.
- CT Log Inclusion: The certificate is sent to a Certificate Transparency (CT) log as a precertificate. The log returns a Signed Certificate Timestamp (SCT), which is embedded in the certificate before a final signature.
- Delivery: The final certificate containing the embedded SCT is returned to the client.
- Certificate Request Input: The client submits an OIDC identity token (JWT), a public key, and a signed challenge (a signature of the
Understand Signed Certificate Timestamps (SCT) in Fulcio
mainFulcio maintains a Certificate Transparency (CT) log using Trillian to ensure all issued certificates are publicly verifiable.
A Signed Certificate Timestamp (SCT) is a cryptographic promise from the CT server that a certificate entry will be included in the log within a fixed timeframe.
SCTs are handled in two ways:
- Detached SCTs: Fulcio returns the SCT alongside the certificate. The caller (e.g., Cosign) is responsible for storing it. If the caller does not store the SCT, it cannot be verified later.
- Embedded SCTs: The SCT is included directly within a certificate extension. This is the preferred method for long-term verification because the SCT travels with the certificate.
All Fulcio signing backends support detached SCTs, but support for embedded SCTs is optional per backend.
How Fulcio's security model works
mainFulcio's security model is built on the assumption that a valid OIDC token provides sufficient proof of ownership for an email address. To mitigate the risk of OIDC provider compromise or malicious CA behavior, Fulcio uses a transparency-based model:
- Mandatory Logging: Fulcio must publish all issued certificates to an immutable, append-only, cryptographically verifiable transparency log (Rekor).
- Client Verification: Clients must not trust any certificate that is not present in the transparency log.
- Detection: This mechanism allows users to detect mis-issued certificates. When combined with Rekor's signature transparency, artifacts signed with compromised accounts can be identified.
This approach shifts the security focus from preventing issuance to ensuring visibility and auditability.
Configure Certificate Transparency (CT) Log support
mainAll signing backends can be configured to write issued certificates to a transparency log. When configured, Signed Certificate Timestamps (SCTs) are returned in a custom HTTP header or gRPC field.
Embedded SCTs: Currently, only the KMS and File-based signing backends support embedded SCTs. Using embedded SCTs is recommended because clients can verify proof of inclusion directly from the certificate without needing to store a detached SCT.
Reuse existing certificates in a hierarchy
mainTo maintain a stable certificate chain (e.g., keeping a long-lived Root CA while rotating Leaf certificates), use the
--existing-root-certand--existing-intermediate-certflags (orEXISTING_ROOT_CERTandEXISTING_INTERMEDIATE_CERTenvironment variables).Decision Logic for Reuse
- Fresh Start: Generate all (Root + Intermediate + Leaf).
- Keep Root, Rotate Intermediate/Leaf: Use
--existing-root-cert. This generates a new intermediate and leaf signed by the existing root. - Keep Root & Intermediate, Rotate Leaf: Use
--existing-root-certAND--existing-intermediate-cert. This generates only a new leaf. - Direct Leaf from Root: Use
--existing-root-certbut do NOT provide an intermediate key ID. This generates a leaf signed directly by the root.
Important Constraints:
- The existing certificate's public key MUST match the KMS key specified. The tool validates this match before proceeding.
--templateflags and--existing-...-certflags are mutually exclusive for the same certificate tier.
# Example: Reuse Root and Intermediate to generate a new Leaf only (GCP KMS) ./certificate-maker create "https://fulcio.example.com" \ --kms-type gcpkms \ --gcp-credentials-file ~/.config/gcloud/application_default_credentials.json \ --root-key-id projects/<project>/locations/<loc>/keyRings/<ring>/cryptoKeys/root/cryptoKeyVersions/1 \ --existing-root-cert ./existing-root.pem \ --intermediate-key-id projects/<project>/locations/<loc>/keyRings/<ring>/cryptoKeys/intermediate/cryptoKeyVersions/1 \ --existing-intermediate-cert ./existing-intermediate.pem \ --leaf-key-id projects/<project>/locations/<loc>/keyRings/<ring>/cryptoKeys/leaf/cryptoKeyVersions/1 \ --leaf-cert leaf.pemSigstore OID extension formats
mainFulcio uses the Sigstore Private Enterprise Number (
1.3.6.1.4.1.57264) to organize metadata in certificates. The encoding format of these extensions varies depending on the OID:- Raw Strings: OIDs
1.3.6.1.4.1.57264.1.1through1.3.6.1.4.1.57264.1.6are formatted as raw strings without DER encoding. - OtherName SAN: OID
1.3.6.1.4.1.57264.1.7is a DER-encoded string in theSubjectAlternativeNameextension (per RFC 5280 4.2.1.6). - DER-encoded UTF8Strings: OIDs
1.3.6.1.4.1.57264.1.8through1.3.6.1.4.1.57264.1.24are DER-encoded strings using theUTF8String(0x0C) ASN.1 tag.
- Raw Strings: OIDs
How Fulcio uses OIDC tokens
mainFulcio uses OpenID Connect (OIDC) tokens to authenticate requests. During the authentication process, Fulcio extracts subject-related claims from the provided OIDC token and includes them in the issued certificates as Subject Alternative Names (SANs).
Sigstore typically runs a federated OIDC identity provider called Dex. In this flow, users authenticate to their preferred identity provider (e.g., Google, GitHub), and Dex generates a new OIDC token containing claims derived from the original provider's token. Fulcio can also be configured to support OIDC tokens from additional, explicitly configured issuers.
OIDC requirements for URI and Username identities
mainFulcio supports identities based on URIs or plain usernames, requiring a
SubjectDomainin the Fulcio configuration.URI Identity
- Token Claim:
submust be a URI (e.g.,https://example.com/users/1). - Constraint: The domain of the
subclaim must match theSubjectDomainconfiguration exactly. The issuer must partially match the domain (scheme, TLD, and second-level domain). - Result:
subis included as a SAN URI.
Username Identity
- Token Claim:
subis a username (e.g.,exampleUsername). - Constraint: The issuer must partially match the domain (TLD and second-level domain). The
SubjectDomainis appended tosubto form the identity:sub!SubjectDomain(e.g.,exampleUsername!example.com), which is included as an OtherName SAN.
// URI Example { "sub": "https://example.com/users/1" } // Username Example { "sub": "exampleUsername" }- Token Claim: