Skip to content

Commit

Permalink
Add custom-certificate-validator example (#351)
Browse files Browse the repository at this point in the history
* Add certificate-revocation example

This adds an example of how to use a custom TrustedMaterial to implement a Certificate Revocation List

Signed-off-by: Cody Soyland <[email protected]>

* Rename and clarify purpose of example

Signed-off-by: Cody Soyland <[email protected]>

---------

Signed-off-by: Cody Soyland <[email protected]>
  • Loading branch information
codysoyland authored Dec 20, 2024
1 parent da38613 commit 14721fa
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
9 changes: 9 additions & 0 deletions examples/custom-certificate-validator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Custom Certificate Validation example

This example demonstrates how to use a custom TrustedMaterial that implements a custom certificate validator.

This can be used by organizations running private PKI infrastructure to validate certificates issued by that infrastructure, or to implement a custom certificate revocation list (CRL).

This custom TrustedMaterial type wraps any other TrustedMaterial (such as that provided by the Public Good Instance) and acts as a middleware that checks the CRL before the leaf certificate is verified by the wrapped TrustedMaterial.

The code is implemented in `NewValidatingTrustedMaterial`, in `certificate_validator.go`. The unit test in `certificate_validator_test.go` demonstrates how it can be used.
73 changes: 73 additions & 0 deletions examples/custom-certificate-validator/certificate_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"crypto/x509"
"fmt"
"math/big"
"time"

"github.com/sigstore/sigstore-go/pkg/root"
)

type CertificateValidator func(cert *x509.Certificate) error

type ValidatingCertificateAuthority struct {
root.CertificateAuthority
validator CertificateValidator
}

func (ca *ValidatingCertificateAuthority) Verify(leafCert *x509.Certificate, observerTimestamp time.Time) ([][]*x509.Certificate, error) {
if err := ca.validator(leafCert); err != nil {
return nil, fmt.Errorf("certificate validation failed: %w", err)
}
return ca.CertificateAuthority.Verify(leafCert, observerTimestamp)
}

type TrustedMaterialWithCertificateValidator struct {
root.TrustedMaterial
validator CertificateValidator
}

func NewTrustedMaterialWithCertificateValidation(tm root.TrustedMaterial, validator CertificateValidator) *TrustedMaterialWithCertificateValidator {
return &TrustedMaterialWithCertificateValidator{
TrustedMaterial: tm,
validator: validator,
}
}

func (tm *TrustedMaterialWithCertificateValidator) FulcioCertificateAuthorities() []root.CertificateAuthority {
cas := make([]root.CertificateAuthority, len(tm.TrustedMaterial.FulcioCertificateAuthorities()))
for i, ca := range tm.TrustedMaterial.FulcioCertificateAuthorities() {
cas[i] = &ValidatingCertificateAuthority{ca, tm.validator}
}
return cas
}

// NewValidatingTrustedMaterial creates a TrustedMaterial that validates certificates against a list of revoked serial numbers.
func NewValidatingTrustedMaterial(trustedMaterial root.TrustedMaterial, revokedSerialNumbers []*big.Int) root.TrustedMaterial {
return &TrustedMaterialWithCertificateValidator{
TrustedMaterial: trustedMaterial,
validator: func(cert *x509.Certificate) error {
for _, serialNumber := range revokedSerialNumbers {
if cert.SerialNumber.Cmp(serialNumber) == 0 {
return fmt.Errorf("certificate with serial number %v is revoked", serialNumber)
}
}
return nil
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"math/big"
"testing"
"time"

"github.com/sigstore/sigstore-go/pkg/testing/ca"
"github.com/sigstore/sigstore-go/pkg/verify"
"github.com/stretchr/testify/assert"
)

func TestCRLWrapper(t *testing.T) {
virtualSigstore, err := ca.NewVirtualSigstore()
assert.NoError(t, err)

leaf, _, err := virtualSigstore.GenerateLeafCert("[email protected]", "issuer")
assert.NoError(t, err)

trustedMaterial := NewValidatingTrustedMaterial(virtualSigstore, []*big.Int{})
validatingTrustedMaterial := NewValidatingTrustedMaterial(virtualSigstore, []*big.Int{leaf.SerialNumber})

_, err = verify.VerifyLeafCertificate(time.Now(), leaf, trustedMaterial)
assert.NoError(t, err)

_, err = verify.VerifyLeafCertificate(time.Now(), leaf, validatingTrustedMaterial)
assert.Error(t, err)
}

0 comments on commit 14721fa

Please sign in to comment.