diff --git a/authority/root.go b/authority/root.go index 37038cfa3..0a6ee639c 100644 --- a/authority/root.go +++ b/authority/root.go @@ -57,3 +57,26 @@ func (a *Authority) GetFederation() (federation []*x509.Certificate, err error) }) return } + +// GetIntermediateCertificate return the intermediate certificate that issues +// the leaf certificates in the CA. +// +// This method can return nil if the CA is configured with a Certificate +// Authority Service (CAS) that does not implement the +// CertificateAuthorityGetter interface. +func (a *Authority) GetIntermediateCertificate() *x509.Certificate { + if len(a.intermediateX509Certs) > 0 { + return a.intermediateX509Certs[0] + } + return nil +} + +// GetIntermediateCertificates returns a list of all intermediate certificates +// configured. The first certificate in the list will be the issuer certificate. +// +// This method can return an empty list or nil if the CA is configured with a +// Certificate Authority Service (CAS) that does not implement the +// CertificateAuthorityGetter interface. +func (a *Authority) GetIntermediateCertificates() []*x509.Certificate { + return a.intermediateX509Certs +} diff --git a/authority/root_test.go b/authority/root_test.go index e570b0bed..a0811dd2b 100644 --- a/authority/root_test.go +++ b/authority/root_test.go @@ -2,15 +2,18 @@ package authority import ( "crypto/x509" + "crypto/x509/pkix" "errors" "net/http" "reflect" "testing" - "go.step.sm/crypto/pemutil" - "github.com/smallstep/assert" "github.com/smallstep/certificates/api/render" + "github.com/stretchr/testify/require" + "go.step.sm/crypto/keyutil" + "go.step.sm/crypto/minica" + "go.step.sm/crypto/pemutil" ) func TestRoot(t *testing.T) { @@ -152,3 +155,63 @@ func TestAuthority_GetFederation(t *testing.T) { }) } } + +func TestAuthority_GetIntermediateCertificate(t *testing.T) { + ca, err := minica.New(minica.WithRootTemplate(`{ + "subject": {{ toJson .Subject }}, + "issuer": {{ toJson .Subject }}, + "keyUsage": ["certSign", "crlSign"], + "basicConstraints": { + "isCA": true, + "maxPathLen": -1 + } + }`), minica.WithIntermediateTemplate(`{ + "subject": {{ toJson .Subject }}, + "keyUsage": ["certSign", "crlSign"], + "basicConstraints": { + "isCA": true, + "maxPathLen": 1 + } + }`)) + require.NoError(t, err) + + signer, err := keyutil.GenerateDefaultSigner() + require.NoError(t, err) + + cert, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "MiniCA Intermediate CA 0"}, + PublicKey: signer.Public(), + BasicConstraintsValid: true, + IsCA: true, + MaxPathLen: 0, + }) + require.NoError(t, err) + + type fields struct { + intermediateX509Certs []*x509.Certificate + } + tests := []struct { + name string + fields fields + want *x509.Certificate + wantSlice []*x509.Certificate + }{ + {"ok one", fields{[]*x509.Certificate{ca.Intermediate}}, ca.Intermediate, []*x509.Certificate{ca.Intermediate}}, + {"ok multiple", fields{[]*x509.Certificate{cert, ca.Intermediate}}, cert, []*x509.Certificate{cert, ca.Intermediate}}, + {"ok empty", fields{[]*x509.Certificate{}}, nil, []*x509.Certificate{}}, + {"ok nil", fields{nil}, nil, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Authority{ + intermediateX509Certs: tt.fields.intermediateX509Certs, + } + if got := a.GetIntermediateCertificate(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Authority.GetIntermediateCertificate() = %v, want %v", got, tt.want) + } + if got := a.GetIntermediateCertificates(); !reflect.DeepEqual(got, tt.wantSlice) { + t.Errorf("Authority.GetIntermediateCertificates() = %v, want %v", got, tt.wantSlice) + } + }) + } +}