Skip to content

Commit

Permalink
Update certificate issuance documentation
Browse files Browse the repository at this point in the history
Updated with new backends, CSR, embedded SCT, and cleaned up security
model.

Fixes #699

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
  • Loading branch information
haydentherapper committed Jul 24, 2022
1 parent d9fe79b commit ae4c766
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 64 deletions.
102 changes: 58 additions & 44 deletions docs/how-certificate-issuing-works.md
@@ -1,8 +1,7 @@
# Certificate Issuing Overview

This document walks through the process of issuing a code signing certificate
from start to finish. This is a great entry point to understanding how Fulcio
works if you're interested in contributing to the project or just want to
This document walks through the process of issuing a code signing certificate. This is a great entry point to understanding how Fulcio
works if you're interested in contributing to the project or want to
learn more about what's happening under the hood.

## 1 | Certificate Request Input
Expand All @@ -11,17 +10,21 @@ To begin, the client submits a certificate request to Fulcio.

![Certificate request diagram](img/certificate-request.png)

The certificate request contains three items:
The certificate request contains the following:

- An OIDC identity token. This is a JWT containing information about the
principal (identity of our client), the issuer (who verified this identity?
Google, Github etc) and some other metadata.
- The public key. This is the public half of an asymmetric key-pair generated
by the client. The public key will be embedded in the final x509 certificate
if this request is successful
- A challenge. This challenge proves the client is in possession of the private
key that corresponds to the public key provided. The challenge created by
signing the subject portion of the OIDC ID token
- An OpenID Connect (OIDC) identity token. This is a signed JWT containing information about the
principal (identity of the client), the issuer (who issued the identity token -
Google, Microsoft, GitHub, etc.) and additional metadata such as expiration. The princaipl identity
can either be a maintainer identity in the form of an email, or a workload identity.
- The public key. This is the public portion of a cryptographic key pair generated
by the client. The public key will be embedded in the issued X.509 certificate.
- A signed challenge. This challenge proves the client is in possession of the private
key that corresponds to the public key provided. The challenge is created by
signing the subject (`sub`) of the OIDC identity token.
- Alternatively, instead of a public key and signed challenge, a client can provide a certificate
signing request (CSR), which also provides a proof of possession and the public key.

See the [service definition](https://github.com/sigstore/fulcio/blob/main/fulcio.proto) for more details.

## 2 | Authentication

Expand All @@ -30,7 +33,7 @@ OIDC ID token.

![OIDC Authentication diagram](img/authenticate-token.png)

To authorize the token Fulcio must:
To authenticate the token Fulcio must:

- Use the issuer claim from the token to find the issuer's OIDC discovery
endpoint
Expand All @@ -41,61 +44,72 @@ To authorize the token Fulcio must:

Once the client has been authenticated, the next step is to verify the client
is in possession of the private key of the public key they’ve submitted. To do
this we must verify the challenge. This is simply a signature of the `sub`
claim so we verify the signature using the public key supplied.
this, Fulcio verifies the signed challenge or CSR. For a signed challenge, this is
a signature of the `sub` claim. For challenge and CSR are verified using the provided public key.

![Challenge verification diagram](img/verify-challenge.png)

## 4 | Constructing a certificate

The client is now authorized and has proved they own their private key so we
can issue a code signing certificate for them.
The client is now authenticated and has proved possession of the private key. Fulcio now
issues a code signing certificate for the identity from the ID token.

![Certificate contruction diagram](img/create-certificate.png)

At a high level this looks like
At a high level, this consists of:

- Embedded the provided public key in the certificate
- Setting the subject alternative names to match the `sub` claim from the OIDC
ID token (NB: this is email or URI depending on the type of issuer. Email for
Google, but URI for SPIFFE for example)
- Setting various other customer x509 extensions depending on the metadata in
the OIDC ID token claims (e.g the github tag or commit etc)
- Embedding the provided public key in the certificate
- Setting the certificate's subject alternative name (who the certificate is issued for) to
match the subject from the OIDC ID token. This could be an email, SPIFFE ID, or GitHub Actions workflow identity.
- Including the OIDC ID token issuer in a custom field in the certificate
- Setting various X.509 extensions depending on the metadata in
the OIDC ID token claims (e.g GitHub Actions workflow information)

## 5 | Signing the certificate

Our code signing certificate is now complete in detail, but needs to be signed
by certificate authority so that it becomes connected to Fulcio's chain of
trust.
The code signing certificate is now populated, but needs to be signed
by the certificate authority. This will form a chain of trust from the issued
certificate to the certificate authority root certificate.

![Signing diagram](img/sign-certificate.png)

Fulcio supports several certificate authority backends:
Fulcio supports several signing backends to sign certificates:

- KMS: A KMS key hosted on GCP, AWS, Azure or Hashicorp
- [Tink](https://github.com/google/tink): A secure KMS-encrypted Tink keyset created with [tinkey](https://github.com/google/tink/blob/master/docs/TINKEY.md)
- PKCS#11: This works with any PKCS#11 devices including AWS CloudHSM,
[softHSM] and others
- Google Private CA: A hosted certificate authority create by Google Cloud
Platform
- Files: A simple private key and certificate on disk
- Ephemeral: An in-memory key-pair generated on start up
[softHSM](https://www.opendnssec.org/softhsm/) and others
- [Google CA Service](https://cloud.google.com/certificate-authority-service/docs): A GCP-hosted certificate authority
- Files: An on-disk password-protected private key
- Ephemeral (for testing): An in-memory key pair generated on start up

See [Setting up a Fulcio instance](setup.md) for more details.

[softHSM]: https://www.opendnssec.org/softhsm/
## 6 | Certificate Transparency log inclusion

## 6 | Certificate Transparency log upload
As part of certificate issuance, the certificate will be appended to an immutable, append-only,
cryptographically verifiable certificate transparency (CT) log, which allows for issuance to be
publicly auditable.

Once the certificate is signed, there is one final task to complete before
returning the certificate to the client: upload to a certificate transparency
log.
A special X.509 extension, called a poison extension, is included in the certificate before it
is appended to the log. This certificate is called a precertificate, and is not yet usuable by clients.

![Transparency log upload diagram](img/ctlog-upload.png)

The certificate transparency log returns a _Signed Certificate Timestamp_
(SCT). The SCT indicates the log index for the certificate, a timestamp of
when it was included and the whole thing is signed by the certificate
transparency log.
(SCT). The SCT is a promise of inclusion in the log, signed by the CT log. It can be
verified without accessing the log, though a client can also request a cryptographic proof
of inclusion directly from the log.

## 7 | Return certificate to client
The SCT is embedded within the certificate, and signed again.

![Signing with embedded SCT diagram](img/sign-certificate-sct.png)

Finally, we return both the certificate and SCT to the client!
Note that the Certificate Transparency (CT) log is separate from the [Rekor](https://github.com/sigstore/rekor)
transparency log. Fulcio's CT Log only stores issued certificates, while Rekor stores artifact signatures and attestations.

See [Certificate Transparency Log Information](ctlog.md) for more details.

## 7 | Return certificate to client

![Return certificate diagram](img/return-cert.png)
Binary file modified docs/img/ctlog-upload.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/return-cert.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/sign-certificate-sct.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 17 additions & 20 deletions docs/security-model.md
Expand Up @@ -3,30 +3,34 @@
Fulcio assumes that a valid OIDC token is a sufficient "proof of ownership" of
an email address.

To mitigate against this, Fulcio uses a transparency log to help protect
against OIDC compromise. This means:
To mitigate against OIDC compromise, Fulcio appends certificates to an immutable,
append-only cryptographically verifiable transparency log.

- Fulcio MUST publish all certificates to the log.
- Clients MUST NOT trust certificates that are not in the log.

As a result users can detect any mis-issued certificates.
As a result users can detect any mis-issued certificates, either due to the CA
acting maliciously or a compromised OIDC identity provider.

Combined with `rekor's` signature transparency, artifacts signed with
Combined with [Rekor's](https://github.com/sigstore/rekor) signature transparency, artifacts signed with
compromised accounts can be identified.

## Revocation, Rotation and Expiry

Fulcio is designed to avoid the need for revocation of certificates. The Sigstore
ecosystem is designed to avoid the need for maintainers to frequently re-sign artifacts.

### Long-term Certificates

These certificates are typically valid for years. All old code must be
re-signed with a new cert before an old cert expires. Typically this works
with long deprecation periods, dual-signing and planned rotations.
These certificates are typically valid for years. All artifacts must be
re-signed with a new certficate before an old certificate expires. Typically this requires
long deprecation periods, dual-signing and planned rotations.

There are a couple problems with this approach:

1. It assumes users can keep acess to private keys and keep them secret over
long periods of time
2. Revocation is hard and doesn't work well
1. It requires that users can maintain access to private keys and keep them secure over
long periods of time.
2. Revocation doesn't scale well.

### Fulcio's Model

Expand All @@ -39,13 +43,6 @@ Sigstore accomplishes this with a tranpsarency log called
inclusion time in the log was during the certificate's validity period.

An entry in Rekor provides a single-party attestation that a piece of data
existed prior to a certain time. These timestamps cannot be tampered with
later, providing long-term trust. This long-term trust also requires that the
log is monitored.

Transparency Logs make it hard to forge timestamps long-term, but in short
time-windows it would be much easier for the Rekor operator to fake or forge
timestamps. To mitigate this, Rekor's timestamps and tree head are signed - a
valid Signed Tree Head (STH) contains a non-repudiadable timestamp. These
signed timestamp tokens are saved as evidence in case Rekor's clock changes in
the future. So, more skeptical parties don't have to trust Rekor at all!
existed prior to a certain time. These timestamps cannot be tampered with
later, providing long-term trust. This long-term trust also requires that the
log is monitored.

0 comments on commit ae4c766

Please sign in to comment.