diff --git a/authority/authority.go b/authority/authority.go index 98037cc0..4518abdf 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -164,7 +164,7 @@ func (a *Authority) init() error { // Read intermediate and create X509 signer for default CAS. if options.Is(casapi.SoftCAS) { - options.Issuer, err = pemutil.ReadCertificate(a.config.IntermediateCert) + options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) if err != nil { return err } diff --git a/authority/options.go b/authority/options.go index da5a8f88..9594f989 100644 --- a/authority/options.go +++ b/authority/options.go @@ -95,9 +95,9 @@ func WithKeyManager(k kms.KeyManager) Option { func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option { return func(a *Authority) error { srv, err := cas.New(context.Background(), casapi.Options{ - Type: casapi.SoftCAS, - Issuer: crt, - Signer: s, + Type: casapi.SoftCAS, + Signer: s, + CertificateChain: []*x509.Certificate{crt}, }) if err != nil { return err diff --git a/authority/tls_test.go b/authority/tls_test.go index 75f9e234..bf629a0d 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -56,7 +56,7 @@ func (m *certificateDurationEnforcer) Enforce(cert *x509.Certificate) error { } func getDefaultIssuer(a *Authority) *x509.Certificate { - return a.x509CAService.(*softcas.SoftCAS).Issuer + return a.x509CAService.(*softcas.SoftCAS).CertificateChain[len(a.x509CAService.(*softcas.SoftCAS).CertificateChain)-1] } func getDefaultSigner(a *Authority) crypto.Signer { @@ -671,7 +671,7 @@ func TestAuthority_Renew(t *testing.T) { intCert, intSigner := generateIntermidiateCertificate(t, rootCert, rootSigner) _a := testAuthority(t) - _a.x509CAService.(*softcas.SoftCAS).Issuer = intCert + _a.x509CAService.(*softcas.SoftCAS).CertificateChain = []*x509.Certificate{intCert} _a.x509CAService.(*softcas.SoftCAS).Signer = intSigner return &renewTest{ auth: _a, @@ -878,7 +878,7 @@ func TestAuthority_Rekey(t *testing.T) { intCert, intSigner := generateIntermidiateCertificate(t, rootCert, rootSigner) _a := testAuthority(t) - _a.x509CAService.(*softcas.SoftCAS).Issuer = intCert + _a.x509CAService.(*softcas.SoftCAS).CertificateChain = []*x509.Certificate{intCert} _a.x509CAService.(*softcas.SoftCAS).Signer = intSigner return &renewTest{ auth: _a, diff --git a/cas/apiv1/options.go b/cas/apiv1/options.go index e8072437..d185d71a 100644 --- a/cas/apiv1/options.go +++ b/cas/apiv1/options.go @@ -23,8 +23,8 @@ type Options struct { // Issuer and signer are the issuer certificate and signer used in SoftCAS. // They are configured in ca.json crt and key properties. - Issuer *x509.Certificate `json:"-"` - Signer crypto.Signer `json:"-"` + CertificateChain []*x509.Certificate + Signer crypto.Signer `json:"-"` // IsCreator is set to true when we're creating a certificate authority. Is // used to skip some validations when initializing a CertificateAuthority. diff --git a/cas/apiv1/options_test.go b/cas/apiv1/options_test.go index 6d601366..2442b0af 100644 --- a/cas/apiv1/options_test.go +++ b/cas/apiv1/options_test.go @@ -70,7 +70,7 @@ func TestOptions_Validate(t *testing.T) { Type: tt.fields.Type, CredentialsFile: tt.fields.CredentialsFile, CertificateAuthority: tt.fields.CertificateAuthority, - Issuer: tt.fields.Issuer, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, Signer: tt.fields.Signer, } if err := o.Validate(); (err != nil) != tt.wantErr { @@ -120,7 +120,7 @@ func TestOptions_Is(t *testing.T) { Type: tt.fields.Type, CredentialsFile: tt.fields.CredentialsFile, CertificateAuthority: tt.fields.CertificateAuthority, - Issuer: tt.fields.Issuer, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, Signer: tt.fields.Signer, } if got := o.Is(tt.args.t); got != tt.want { diff --git a/cas/cas_test.go b/cas/cas_test.go index 6c4c5c41..7b7b5976 100644 --- a/cas/cas_test.go +++ b/cas/cas_test.go @@ -31,8 +31,8 @@ func (m *mockCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1 func TestNew(t *testing.T) { expected := &softcas.SoftCAS{ - Issuer: &x509.Certificate{Subject: pkix.Name{CommonName: "Test Issuer"}}, - Signer: ed25519.PrivateKey{}, + CertificateChain: []*x509.Certificate{{Subject: pkix.Name{CommonName: "Test Issuer"}}}, + Signer: ed25519.PrivateKey{}, } apiv1.Register(apiv1.Type("nockCAS"), func(ctx context.Context, opts apiv1.Options) (apiv1.CertificateAuthorityService, error) { @@ -50,18 +50,18 @@ func TestNew(t *testing.T) { wantErr bool }{ {"ok default", args{context.Background(), apiv1.Options{ - Issuer: &x509.Certificate{Subject: pkix.Name{CommonName: "Test Issuer"}}, - Signer: ed25519.PrivateKey{}, + CertificateChain: []*x509.Certificate{{Subject: pkix.Name{CommonName: "Test Issuer"}}}, + Signer: ed25519.PrivateKey{}, }}, expected, false}, {"ok softcas", args{context.Background(), apiv1.Options{ - Type: "softcas", - Issuer: &x509.Certificate{Subject: pkix.Name{CommonName: "Test Issuer"}}, - Signer: ed25519.PrivateKey{}, + Type: "softcas", + CertificateChain: []*x509.Certificate{{Subject: pkix.Name{CommonName: "Test Issuer"}}}, + Signer: ed25519.PrivateKey{}, }}, expected, false}, {"ok SoftCAS", args{context.Background(), apiv1.Options{ - Type: "SoftCAS", - Issuer: &x509.Certificate{Subject: pkix.Name{CommonName: "Test Issuer"}}, - Signer: ed25519.PrivateKey{}, + Type: "SoftCAS", + CertificateChain: []*x509.Certificate{{Subject: pkix.Name{CommonName: "Test Issuer"}}}, + Signer: ed25519.PrivateKey{}, }}, expected, false}, {"fail empty", args{context.Background(), apiv1.Options{}}, (*softcas.SoftCAS)(nil), true}, {"fail type", args{context.Background(), apiv1.Options{Type: "FailCAS"}}, nil, true}, diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index 4ece82d9..82a4a0a6 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -26,9 +26,9 @@ var now = func() time.Time { // SoftCAS implements a Certificate Authority Service using Golang or KMS // crypto. This is the default CAS used in step-ca. type SoftCAS struct { - Issuer *x509.Certificate - Signer crypto.Signer - KeyManager kms.KeyManager + CertificateChain []*x509.Certificate + Signer crypto.Signer + KeyManager kms.KeyManager } // New creates a new CertificateAuthorityService implementation using Golang or KMS @@ -36,16 +36,16 @@ type SoftCAS struct { func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) { if !opts.IsCreator { switch { - case opts.Issuer == nil: - return nil, errors.New("softCAS 'issuer' cannot be nil") + case opts.CertificateChain == nil: + return nil, errors.New("softCAS 'CertificateChain' cannot be nil") case opts.Signer == nil: return nil, errors.New("softCAS 'signer' cannot be nil") } } return &SoftCAS{ - Issuer: opts.Issuer, - Signer: opts.Signer, - KeyManager: opts.KeyManager, + CertificateChain: opts.CertificateChain, + Signer: opts.Signer, + KeyManager: opts.KeyManager, }, nil } @@ -66,18 +66,16 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 if req.Template.NotAfter.IsZero() { req.Template.NotAfter = t.Add(req.Lifetime) } - req.Template.Issuer = c.Issuer.Subject + req.Template.Issuer = c.CertificateChain[0].Subject - cert, err := x509util.CreateCertificate(req.Template, c.Issuer, req.Template.PublicKey, c.Signer) + cert, err := x509util.CreateCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) if err != nil { return nil, err } return &apiv1.CreateCertificateResponse{ - Certificate: cert, - CertificateChain: []*x509.Certificate{ - c.Issuer, - }, + Certificate: cert, + CertificateChain: c.CertificateChain, }, nil } @@ -93,18 +91,16 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R t := now() req.Template.NotBefore = t.Add(-1 * req.Backdate) req.Template.NotAfter = t.Add(req.Lifetime) - req.Template.Issuer = c.Issuer.Subject + req.Template.Issuer = c.CertificateChain[0].Subject - cert, err := x509util.CreateCertificate(req.Template, c.Issuer, req.Template.PublicKey, c.Signer) + cert, err := x509util.CreateCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer) if err != nil { return nil, err } return &apiv1.RenewCertificateResponse{ - Certificate: cert, - CertificateChain: []*x509.Certificate{ - c.Issuer, - }, + Certificate: cert, + CertificateChain: c.CertificateChain, }, nil } @@ -113,10 +109,8 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R // in the db. func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { return &apiv1.RevokeCertificateResponse{ - Certificate: req.Certificate, - CertificateChain: []*x509.Certificate{ - c.Issuer, - }, + Certificate: req.Certificate, + CertificateChain: c.CertificateChain, }, nil } diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index 258ebf5c..0a50a990 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -182,9 +182,9 @@ func TestNew(t *testing.T) { want *SoftCAS wantErr bool }{ - {"ok", args{context.Background(), apiv1.Options{Issuer: testIssuer, Signer: testSigner}}, &SoftCAS{Issuer: testIssuer, Signer: testSigner}, false}, + {"ok", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}}, &SoftCAS{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}, false}, {"fail no issuer", args{context.Background(), apiv1.Options{Signer: testSigner}}, nil, true}, - {"fail no signer", args{context.Background(), apiv1.Options{Issuer: testIssuer}}, nil, true}, + {"fail no signer", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}}}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -208,11 +208,11 @@ func TestNew_register(t *testing.T) { } want := &SoftCAS{ - Issuer: testIssuer, - Signer: testSigner, + CertificateChain: []*x509.Certificate{testIssuer}, + Signer: testSigner, } - got, err := newFn(context.Background(), apiv1.Options{Issuer: testIssuer, Signer: testSigner}) + got, err := newFn(context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}) if err != nil { t.Errorf("New() error = %v", err) return @@ -286,8 +286,8 @@ func TestSoftCAS_CreateCertificate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &SoftCAS{ - Issuer: tt.fields.Issuer, - Signer: tt.fields.Signer, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, + Signer: tt.fields.Signer, } got, err := c.CreateCertificate(tt.args.req) if (err != nil) != tt.wantErr { @@ -342,8 +342,8 @@ func TestSoftCAS_RenewCertificate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &SoftCAS{ - Issuer: tt.fields.Issuer, - Signer: tt.fields.Signer, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, + Signer: tt.fields.Signer, } got, err := c.RenewCertificate(tt.args.req) if (err != nil) != tt.wantErr { @@ -395,8 +395,8 @@ func TestSoftCAS_RevokeCertificate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &SoftCAS{ - Issuer: tt.fields.Issuer, - Signer: tt.fields.Signer, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, + Signer: tt.fields.Signer, } got, err := c.RevokeCertificate(tt.args.req) if (err != nil) != tt.wantErr { @@ -524,9 +524,9 @@ func TestSoftCAS_CreateCertificateAuthority(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &SoftCAS{ - Issuer: tt.fields.Issuer, - Signer: tt.fields.Signer, - KeyManager: tt.fields.KeyManager, + CertificateChain: []*x509.Certificate{tt.fields.Issuer}, + Signer: tt.fields.Signer, + KeyManager: tt.fields.KeyManager, } got, err := c.CreateCertificateAuthority(tt.args.req) if (err != nil) != tt.wantErr {