Skip to content

Commit

Permalink
fileca: add support for intermediate certificates
Browse files Browse the repository at this point in the history
Signed-off-by: Nathan Smith <nathan@nfsmith.ca>
  • Loading branch information
nsmith5 committed Jan 18, 2022
1 parent 86a2036 commit e8d79db
Show file tree
Hide file tree
Showing 24 changed files with 713 additions and 225 deletions.
22 changes: 11 additions & 11 deletions pkg/ca/fileca/fileca.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ import (
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"sync"

"github.com/fsnotify/fsnotify"
"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/ca/x509ca"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

type fileCA struct {
sync.RWMutex

cert *x509.Certificate
key crypto.Signer
certs []*x509.Certificate
key crypto.Signer
}

// NewFileCA returns a file backed certificate authority. Expects paths to a
Expand All @@ -43,7 +43,7 @@ func NewFileCA(certPath, keyPath, keyPass string, watch bool) (ca.CertificateAut
var fca fileCA

var err error
fca.cert, fca.key, err = loadKeyPair(certPath, keyPath, keyPass)
fca.certs, fca.key, err = loadKeyPair(certPath, keyPath, keyPass)
if err != nil {
return nil, err
}
Expand All @@ -68,21 +68,21 @@ func NewFileCA(certPath, keyPath, keyPass string, watch bool) (ca.CertificateAut
return &fca, err
}

func (fca *fileCA) updateX509KeyPair(cert *x509.Certificate, key crypto.Signer) {
func (fca *fileCA) updateX509KeyPair(certs []*x509.Certificate, key crypto.Signer) {
fca.Lock()
defer fca.Unlock()

// NB: We use the RWLock to unsure a reading thread can't get a mismatching
// cert / key pair by reading the attributes halfway through the update
// below.
fca.cert = cert
fca.certs = certs
fca.key = key
}

func (fca *fileCA) getX509KeyPair() (*x509.Certificate, crypto.Signer) {
fca.RLock()
defer fca.RUnlock()
return fca.cert, fca.key
return fca.certs[0], fca.key
}

// CreateCertificate issues code signing certificates
Expand All @@ -103,8 +103,8 @@ func (fca *fileCA) CreateCertificate(_ context.Context, subject *challenges.Chal
}

func (fca *fileCA) Root(ctx context.Context) ([]byte, error) {
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: fca.cert.Raw,
}), nil
fca.RLock()
defer fca.RUnlock()

return cryptoutils.MarshalCertificatesToPEM(fca.certs)
}
65 changes: 52 additions & 13 deletions pkg/ca/fileca/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,64 @@ import (
"go.step.sm/crypto/pemutil"
)

func loadKeyPair(certPath, keyPath, keyPass string) (*x509.Certificate, crypto.Signer, error) {
func loadKeyPair(certPath, keyPath, keyPass string) ([]*x509.Certificate, crypto.Signer, error) {

var (
cert *x509.Certificate
err error
key crypto.Signer
certs []*x509.Certificate
err error
key crypto.Signer
)

// TODO: Load chain of certs (intermediates and root) instead of just one
// certificate.
cert, err = pemutil.ReadCertificate(certPath)
// NB: certs are ordered from leaf at certs[0] to root at
// certs[len(certs)-1]
certs, err = pemutil.ReadCertificateBundle(certPath)
if err != nil {
return nil, nil, err
}

// Verify certificate chain
{
roots := x509.NewCertPool()
roots.AddCert(certs[len(certs)-1])

intermediates := x509.NewCertPool()
if len(certs) > 2 {
for _, intermediate := range certs[1 : len(certs)-1] {
intermediates.AddCert(intermediate)
}
}

opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{
x509.ExtKeyUsageCodeSigning,
},
}
if _, err := certs[0].Verify(opts); err != nil {
return nil, nil, err
}

if !certs[0].IsCA {
return nil, nil, errors.New(`fileca: certificate is not a CA`)
}

// If using an intermediate, verify that code signing extended key
// usage is set to satify extended key usage chainging
if len(certs) > 1 {
var hasExtKeyUsageCodeSigning bool
for _, extKeyUsage := range certs[0].ExtKeyUsage {
if extKeyUsage == x509.ExtKeyUsageCodeSigning {
hasExtKeyUsageCodeSigning = true
break
}
}
if !hasExtKeyUsageCodeSigning {
return nil, nil, errors.New(`fileca: certificate must have extended key usage code signing set to sign code signing certificates`)
}
}
}

{
opaqueKey, err := pemutil.Read(keyPath, pemutil.WithPassword([]byte(keyPass)))
if err != nil {
Expand All @@ -55,15 +98,11 @@ func loadKeyPair(certPath, keyPath, keyPass string) (*x509.Certificate, crypto.S
}
}

if !valid(cert, key) {
if !valid(certs[0], key) {
return nil, nil, errors.New(`fileca: certificate public key and private key don't match`)
}

if !cert.IsCA {
return nil, nil, errors.New(`fileca: certificate is not a CA`)
}

return cert, key, nil
return certs, key, nil
}

func valid(cert *x509.Certificate, key crypto.Signer) bool {
Expand Down
4 changes: 4 additions & 0 deletions pkg/ca/fileca/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ func TestValidLoadKeyPair(t *testing.T) {
"ecdsa",
"ed25519",
"rsa4096",
"openssl",
"intermediate-2",
"intermediate-3",
}

for _, keypair := range keypairs {
Expand All @@ -42,6 +45,7 @@ func TestInvalidLoadKeyPair(t *testing.T) {
keypairs := []string{
"notca",
"mismatch",
"eku-chaining-violation",
}

for _, keypair := range keypairs {
Expand Down
19 changes: 9 additions & 10 deletions pkg/ca/fileca/testdata/ecdsa-cert.pem
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBxzCCAU6gAwIBAgIUbQqE8rDPWDqJexmvpaeamgZe/HIwCgYIKoZIzj0EAwIw
EDEOMAwGA1UEAwwFZWNkc2EwIBcNMjExMjIxMTkxMzI2WhgPMjEyMTExMjcxOTEz
MjZaMBAxDjAMBgNVBAMMBWVjZHNhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMV9i
0e3Ld1eQy9UXII5MOymw2IFBo288zuOMeH+7w0ejJlY0PFowY4rItKIhqRIWOqFA
luSaVC59sKsqjsiLdQQW2CV19eYFhVvYQS1S2QaROpFA5Zt8ALOACyp5s+6+o2cw
ZTAdBgNVHQ4EFgQU3QF9mKDrefmeiE3lqC46PSmhEOkwHwYDVR0jBBgwFoAU3QF9
mKDrefmeiE3lqC46PSmhEOkwDwYDVR0TAQH/BAUwAwEB/zASBgNVHRMBAf8ECDAG
AQH/AgEBMAoGCCqGSM49BAMCA2cAMGQCMAQ/g18eRvqITDZEKdzf4bI4qKF/ZbVL
GTZ+2HHZYwDvsuHeznTl1Uq1stzmySi4owIwV1jCF8f4gikxT0XCF+u1CJlVYiZP
tyRnLdZaKl/seNUmBO0RRR72tsRd/X1QR3NK
MIIBojCCASmgAwIBAgIRAPtUhtNn5zcLWx6IFhFNbqowCgYIKoZIzj0EAwMwEDEO
MAwGA1UEAxMFZWNkc2EwIBcNMjIwMTE4MjAzOTQ4WhgPMjEyMTEyMjUyMDM5NDda
MBAxDjAMBgNVBAMTBWVjZHNhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdUISPLNy
nh6rLd9mDRTNrFygLajEOwmeIaZu6/OW8wJAM2r50ZvkpO0X5bOmf1ezSnuiYWpQ
VjwQqSJ78zLgV2CHLm1td5g/F/lCIofAY+5w56uJgrzsqrAAGODumWpzo0UwQzAO
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUpqtq
+RZD2VWofB75Lsk67/hlFZcwCgYIKoZIzj0EAwMDZwAwZAIwPjiaLvSRX/ju4rGM
/4Dq+xfwVtlri3zWkGpmNf65ciZipKceKz8wzAYZ6aCoBmSHAjBhnavATf3NxRik
sMDePua/9rleC/gaCVC9l022c8Ht+i2tjhhwmQ09p+vOf8ugXsM=
-----END CERTIFICATE-----
17 changes: 9 additions & 8 deletions pkg/ca/fileca/testdata/ecdsa-key.pem
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIBEzBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIGCeFiJ2rs3wCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECGZQSOBsvcjqBIHAGdU9wj136ikM
QUqJqDt5oSDKJAU2Yrv+pCRLz6VENGUvxFrPsn9fbjSt69fSZMm09mITfEg1eLO4
9LDl7PW1Bza803IXKQnZ0xnRUkkY1GQfDtQEGCYFaojRpYNLmmSiHpeFrqAPz83K
+oXsRTwuRkDrABNpwTCEXYVmcHUmk9NqC6E2qjmYOyDx0ktA4HG62H3/cpGZleBs
l58oyOg658erxF1rnASN15lw9/1g0lWACsXsMgbkjDnY51LU71pR
-----END ENCRYPTED PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,1ee56fe067d83265fe430391edfa6586

W5NqqRe5rOVe4OvxehYKm6wscR1JFoyRyd8M+Rutp8Q2lxPuKFhR4FZ61b0yy6pr
LGJGQWOTIZxrNZ8g4JeS9I3huDWGloZRI2fbTg69HK4EiQQWUc1wS1TWAVoaf4fr
LclBWxp2UzqHDaNJ0/2DoGFZhaeMU84VA1O41lO+p5Cx4bms0yWeEHwOrf2AmnNY
l5Zm9zoPpXxaDEPSTs5c1loRmmxPHKgb68oZPxEnsCg=
-----END EC PRIVATE KEY-----
15 changes: 7 additions & 8 deletions pkg/ca/fileca/testdata/ed25519-cert.pem
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBTzCCAQGgAwIBAgIUeObF4LopbObr0zVOX7BAZbvy4MswBQYDK2VwMBIxEDAO
BgNVBAMMB2VkMjU1MTkwIBcNMjExMjIxMTkxMzI2WhgPMjEyMTExMjcxOTEzMjZa
MBIxEDAOBgNVBAMMB2VkMjU1MTkwKjAFBgMrZXADIQBNNJP9Ys+Sx0Cx/c5pQNAF
cuECdESA0vB2IqXVAG5OiaNnMGUwHQYDVR0OBBYEFJEGm0OzRNsdBVLdDBCcx21i
nEySMB8GA1UdIwQYMBaAFJEGm0OzRNsdBVLdDBCcx21inEySMA8GA1UdEwEB/wQF
MAMBAf8wEgYDVR0TAQH/BAgwBgEB/wIBATAFBgMrZXADQQD6quk/tnnZpFgabR2Q
4WCweJfZ4NfrhMOVvAPdECW/P57NH0P2BUSOK+/DktOBFIjLUWG6ptRExHDcRsFm
WTsA
MIIBKTCB3KADAgECAhEAtBnovvbxszrVoAQ52cyprDAFBgMrZXAwEjEQMA4GA1UE
AxMHZWQyNTUxOTAgFw0yMjAxMTgyMDM5NDVaGA8yMTIxMTIyNTIwMzk0NVowEjEQ
MA4GA1UEAxMHZWQyNTUxOTAqMAUGAytlcAMhAEvHE6tyPrILBpp/trmJFq47084w
PPeuGwL1NUeOs85po0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
/wIBATAdBgNVHQ4EFgQUvGmDwKfk/xiWf+ogGKx6391FPG0wBQYDK2VwA0EARdJM
8/WM/6UC/fUOc0m597lqAiln+XXi1o6IJIwYf849WAwwZoXjcDpO9PM5KM6jg3dB
KecnmX3PBCvtMAF6Bg==
-----END CERTIFICATE-----
7 changes: 4 additions & 3 deletions pkg/ca/fileca/testdata/ed25519-key.pem
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGKME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAggWEDSFylYswICCAAw
DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIaecFy/8IbAYEOHb3xUdAVad3ZcXk
dkwJjtPNP2t2PA/6ngVgfsx2dgqKBhjg9JXG98Yw2eeYqJsbZ4jrHAJK0l8E
MIGkMGAGCSqGSIb3DQEFDTBTMDIGCSqGSIb3DQEFDDAlBBAodg9wkHvRNwsiMIKj
w7HOAgMBhqAwDAYIKoZIhvcNAgkAADAdBglghkgBZQMEASoEEJ/dx5SGu+D6gLpq
tNb/o1EEQKKnS4CPbd8fqBfCXebZzq2+yzEmp8LNwQjA4/di+MwqO+Alr/w9QqUC
MwKOP0K/c4Rul3Gdj1TJeB79gk6+8VY=
-----END ENCRYPTED PRIVATE KEY-----
31 changes: 31 additions & 0 deletions pkg/ca/fileca/testdata/eku-chaining-violation-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIBhTCCATegAwIBAgIQaX/zl+RgBGmPB8xB5p/AGzAFBgMrZXAwGDEWMBQGA1UE
AxMNaW50ZXJtZWRpYXRlMTAgFw0yMjAxMTgyMDQwMDBaGA8yMTIxMTIyNTIwNDAw
MFowGDEWMBQGA1UEAxMNaW50ZXJtZWRpYXRlMjBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABNqxk7mTvl1Z3Lu3EPPuMRH1g0bWyuO7mtSY3iIxRUReGsd8E+OentZj
U0WHIkJr5fW6dmsGLq0O8Ga64VOufyCjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNV
HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQNtutvnfDRxsfmk8BQM1ZJLBZbvDAf
BgNVHSMEGDAWgBSjru3PBZQwhlED2N7kkNC5on99djAFBgMrZXADQQC5Fj8miHDD
nlw4v8t6oh11s6UygsF5ZsD+2sm9+GuxGrI0HcFbIyQbPZyV3Wv2ZcTJReZ489mF
lWS367m4jX0D
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBdDCCARqgAwIBAgIRAOcT6tYbqPoC/S3mafr+lCIwCgYIKoZIzj0EAwIwDzEN
MAsGA1UEAxMEcm9vdDAgFw0yMjAxMTgyMDQwMDBaGA8yMTIxMTIyNTIwNDAwMFow
GDEWMBQGA1UEAxMNaW50ZXJtZWRpYXRlMTAqMAUGAytlcAMhAP8ne9tAJLrAAf/L
kQnlS+tiYBvKZfu+hLCQisMfWlFKo3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0l
BAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUo67t
zwWUMIZRA9je5JDQuaJ/fXYwHwYDVR0jBBgwFoAUqCLjqJtiXiediSJebsQdCV59
d9MwCgYIKoZIzj0EAwIDSAAwRQIhALLWa6T98PCO/zO/DXzsY5vLEZmiaeJj6ac3
GQtfXkjFAiBh/GcSFfh6fHn/GPs4N5HZXQs7x4+zTlWD7hSCo2jezg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBYzCCAQmgAwIBAgIQA8PGipZ4WhNTXXeiHs9HkzAKBggqhkjOPQQDAjAPMQ0w
CwYDVQQDEwRyb290MCAXDTIyMDExODIwNDAwMFoYDzIxMjExMjI1MjA0MDAwWjAP
MQ0wCwYDVQQDEwRyb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2a1VgMYP
5Uh/eMs/smjDFjCLEq7UTaj1D36Shp/GrsvXXCKyH/4ZlcVpTaVDC0oDDQ6os4UV
aoT/4Ix1hU5bnqNFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
AQIwHQYDVR0OBBYEFKgi46ibYl4nnYkiXm7EHQlefXfTMAoGCCqGSM49BAMCA0gA
MEUCIQDIuZn/YBfndAo7Rv5FUl/9HM9dr9LrDQFfCkn5UnOYnQIgN6ruDqzt+GHG
/HJ1qe6CfnglgaEJ+/Vloc2PTcm/rVs=
-----END CERTIFICATE-----
8 changes: 8 additions & 0 deletions pkg/ca/fileca/testdata/eku-chaining-violation-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,2e9623757041e0d3ca2814238b20a8a0

in6cGXlJQtojVS3Epbt16OoPR+MIZl+yL2+QMwGszsLHwA0r9OgpyK7cnFQKEJST
m6KVpEWprRU//yljH8XZ0hOB8xzh0XR2Xbp5chMU/MptOjWm4yv4CS2Qvvob/sB2
V9e+KmyUJLe123h/IYcZ5upHQdQqlf8kKu2vdL9sLHM=
-----END EC PRIVATE KEY-----
Loading

0 comments on commit e8d79db

Please sign in to comment.