Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authority.WithX509SignerFunc #879

Merged
merged 2 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions authority/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,22 @@ func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option {
}
}

// WithX509SignerFunc defines the function used to get the chain of certificates
// and signer used when we sign X.509 certificates.
func WithX509SignerFunc(fn func() ([]*x509.Certificate, crypto.Signer, error)) Option {
return func(a *Authority) error {
srv, err := cas.New(context.Background(), casapi.Options{
Type: casapi.SoftCAS,
CertificateSigner: fn,
})
if err != nil {
return err
}
a.x509CAService = srv
return nil
}
}

// WithSSHUserSigner defines the signer used to sign SSH user certificates.
func WithSSHUserSigner(s crypto.Signer) Option {
return func(a *Authority) error {
Expand Down
9 changes: 7 additions & 2 deletions cas/apiv1/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,18 @@ type Options struct {
// https://cloud.google.com/docs/authentication.
CredentialsFile string `json:"credentialsFile,omitempty"`

// Certificate and signer are the issuer certificate, along with any other
// bundled certificates to be returned in the chain for consumers, and
// CertificateChain and Signer are the issuer certificate, along with any
maraino marked this conversation as resolved.
Show resolved Hide resolved
// other bundled certificates to be returned in the chain for consumers, and
// signer used in SoftCAS. They are configured in ca.json crt and key
// properties.
CertificateChain []*x509.Certificate `json:"-"`
Signer crypto.Signer `json:"-"`

// CertificateSigner combines CertificateChain and Signer in a callback that
// returns the chain of certificate and signer used to sign X.509
// certificates in SoftCAS.
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error) `json:"-"`

// IsCreator is set to true when we're creating a certificate authority. It
// is used to skip some validations when initializing a
// CertificateAuthority. This option is used on SoftCAS and CloudCAS.
Expand Down
58 changes: 42 additions & 16 deletions cas/softcas/softcas.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,28 @@ var now = time.Now
// SoftCAS implements a Certificate Authority Service using Golang or KMS
// crypto. This is the default CAS used in step-ca.
type SoftCAS struct {
CertificateChain []*x509.Certificate
Signer crypto.Signer
KeyManager kms.KeyManager
CertificateChain []*x509.Certificate
Signer crypto.Signer
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
KeyManager kms.KeyManager
}

// New creates a new CertificateAuthorityService implementation using Golang or KMS
// crypto.
func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) {
if !opts.IsCreator {
switch {
case len(opts.CertificateChain) == 0:
case len(opts.CertificateChain) == 0 && opts.CertificateSigner == nil:
return nil, errors.New("softCAS 'CertificateChain' cannot be nil")
case opts.Signer == nil:
case opts.Signer == nil && opts.CertificateSigner == nil:
return nil, errors.New("softCAS 'signer' cannot be nil")
}
}
return &SoftCAS{
CertificateChain: opts.CertificateChain,
Signer: opts.Signer,
KeyManager: opts.KeyManager,
CertificateChain: opts.CertificateChain,
Signer: opts.Signer,
CertificateSigner: opts.CertificateSigner,
KeyManager: opts.KeyManager,
}, nil
}

Expand All @@ -57,23 +59,29 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
}

t := now()

// Provisioners can also set specific values.
if req.Template.NotBefore.IsZero() {
req.Template.NotBefore = t.Add(-1 * req.Backdate)
}
if req.Template.NotAfter.IsZero() {
req.Template.NotAfter = t.Add(req.Lifetime)
}
req.Template.Issuer = c.CertificateChain[0].Subject

cert, err := createCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer)
chain, signer, err := c.getCertSigner()
if err != nil {
return nil, err
}
req.Template.Issuer = chain[0].Subject

cert, err := createCertificate(req.Template, chain[0], req.Template.PublicKey, signer)
if err != nil {
return nil, err
}

return &apiv1.CreateCertificateResponse{
Certificate: cert,
CertificateChain: c.CertificateChain,
CertificateChain: chain,
}, nil
}

Expand All @@ -89,26 +97,35 @@ 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.CertificateChain[0].Subject

cert, err := createCertificate(req.Template, c.CertificateChain[0], req.Template.PublicKey, c.Signer)
chain, signer, err := c.getCertSigner()
if err != nil {
return nil, err
}
req.Template.Issuer = chain[0].Subject

cert, err := createCertificate(req.Template, chain[0], req.Template.PublicKey, signer)
if err != nil {
return nil, err
}

return &apiv1.RenewCertificateResponse{
Certificate: cert,
CertificateChain: c.CertificateChain,
CertificateChain: chain,
}, nil
}

// RevokeCertificate revokes the given certificate in step-ca. In SoftCAS this
// operation is a no-op as the actual revoke will happen when we store the entry
// in the db.
func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
chain, _, err := c.getCertSigner()
if err != nil {
return nil, err
}
return &apiv1.RevokeCertificateResponse{
Certificate: req.Certificate,
CertificateChain: c.CertificateChain,
CertificateChain: chain,
}, nil
}

Expand Down Expand Up @@ -179,7 +196,7 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori
}, nil
}

// initializeKeyManager initiazes the default key manager if was not given.
// initializeKeyManager initializes the default key manager if was not given.
func (c *SoftCAS) initializeKeyManager() (err error) {
if c.KeyManager == nil {
c.KeyManager, err = kms.New(context.Background(), kmsapi.Options{
Expand All @@ -189,6 +206,15 @@ func (c *SoftCAS) initializeKeyManager() (err error) {
return
}

// getCertSigner returns the certificate chain and signer to use.
func (c *SoftCAS) getCertSigner() ([]*x509.Certificate, crypto.Signer, error) {
if c.CertificateSigner != nil {
return c.CertificateSigner()
}
return c.CertificateChain, c.Signer, nil

}

// createKey uses the configured kms to create a key.
func (c *SoftCAS) createKey(req *kmsapi.CreateKeyRequest) (*kmsapi.CreateKeyResponse, error) {
if err := c.initializeKeyManager(); err != nil {
Expand Down
Loading