Pass issuer and signer to softCAS options.
Remove commented code and initialize CAS properly. Minor fixes in CloudCAS.
This commit is contained in:
parent
c8d9cb0a1d
commit
aad8f9e582
10 changed files with 191 additions and 101 deletions
|
@ -148,18 +148,6 @@ func (a *Authority) init() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the X.509 CA Service if it has not been set in the options
|
|
||||||
if a.x509CAService == nil {
|
|
||||||
var options casapi.Options
|
|
||||||
if a.config.CAS != nil {
|
|
||||||
options = *a.config.CAS
|
|
||||||
}
|
|
||||||
a.x509CAService, err = cas.New(context.Background(), options)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize step-ca Database if it's not already initialized with WithDB.
|
// Initialize step-ca Database if it's not already initialized with WithDB.
|
||||||
// If a.config.DB is nil then a simple, barebones in memory DB will be used.
|
// If a.config.DB is nil then a simple, barebones in memory DB will be used.
|
||||||
if a.db == nil {
|
if a.db == nil {
|
||||||
|
@ -206,6 +194,10 @@ func (a *Authority) init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.x509Issuer = crt
|
||||||
|
|
||||||
|
// Read signer only is the CAS is the default one.
|
||||||
|
if a.config.CAS.HasType(casapi.SoftCAS) {
|
||||||
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||||
SigningKey: a.config.IntermediateKey,
|
SigningKey: a.config.IntermediateKey,
|
||||||
Password: []byte(a.config.Password),
|
Password: []byte(a.config.Password),
|
||||||
|
@ -214,7 +206,39 @@ func (a *Authority) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.x509Signer = signer
|
a.x509Signer = signer
|
||||||
a.x509Issuer = crt
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the X.509 CA Service if it has not been set in the options
|
||||||
|
if a.x509CAService == nil {
|
||||||
|
var options casapi.Options
|
||||||
|
if a.config.CAS != nil {
|
||||||
|
options = *a.config.CAS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set issuer and signer for default CAS.
|
||||||
|
if options.HasType(casapi.SoftCAS) {
|
||||||
|
crt, err := pemutil.ReadCertificate(a.config.IntermediateCert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||||
|
SigningKey: a.config.IntermediateKey,
|
||||||
|
Password: []byte(a.config.Password),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Issuer = crt
|
||||||
|
options.Signer = signer
|
||||||
|
}
|
||||||
|
|
||||||
|
a.x509CAService, err = cas.New(context.Background(), options)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt and load SSH keys
|
// Decrypt and load SSH keys
|
||||||
|
|
|
@ -187,7 +187,7 @@ func (c *Config) Validate() error {
|
||||||
case c.IntermediateCert == "":
|
case c.IntermediateCert == "":
|
||||||
return errors.New("crt cannot be empty")
|
return errors.New("crt cannot be empty")
|
||||||
|
|
||||||
case c.IntermediateKey == "":
|
case c.IntermediateKey == "" && c.CAS.HasType(cas.SoftCAS):
|
||||||
return errors.New("key cannot be empty")
|
return errors.New("key cannot be empty")
|
||||||
|
|
||||||
case len(c.DNSNames) == 0:
|
case len(c.DNSNames) == 0:
|
||||||
|
|
|
@ -145,32 +145,24 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(-1 * signOpts.Backdate))
|
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate))
|
||||||
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
||||||
Template: leaf,
|
Template: leaf,
|
||||||
Issuer: a.x509Issuer,
|
|
||||||
Signer: a.x509Signer,
|
|
||||||
Lifetime: lifetime,
|
Lifetime: lifetime,
|
||||||
|
Backdate: signOpts.Backdate,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
||||||
}
|
}
|
||||||
serverCert := resp.Certificate
|
|
||||||
|
|
||||||
// serverCert, err := x509util.CreateCertificate(leaf, a.x509Issuer, csr.PublicKey, a.x509Signer)
|
if err = a.db.StoreCertificate(resp.Certificate); err != nil {
|
||||||
// if err != nil {
|
|
||||||
// return nil, errs.Wrap(http.StatusInternalServerError, err,
|
|
||||||
// "authority.Sign; error creating certificate", opts...)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if err = a.db.StoreCertificate(serverCert); err != nil {
|
|
||||||
if err != db.ErrNotImplemented {
|
if err != db.ErrNotImplemented {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err,
|
return nil, errs.Wrap(http.StatusInternalServerError, err,
|
||||||
"authority.Sign; error storing certificate in db", opts...)
|
"authority.Sign; error storing certificate in db", opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*x509.Certificate{serverCert, a.x509Issuer}, nil
|
return append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renew creates a new Certificate identical to the old certificate, except
|
// Renew creates a new Certificate identical to the old certificate, except
|
||||||
|
@ -200,13 +192,12 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
||||||
// Durations
|
// Durations
|
||||||
backdate := a.config.AuthorityConfig.Backdate.Duration
|
backdate := a.config.AuthorityConfig.Backdate.Duration
|
||||||
duration := oldCert.NotAfter.Sub(oldCert.NotBefore)
|
duration := oldCert.NotAfter.Sub(oldCert.NotBefore)
|
||||||
now := time.Now().UTC()
|
lifetime := duration - backdate
|
||||||
|
|
||||||
|
// Create new certificate from previous values.
|
||||||
|
// Issuer, NotBefore, NotAfter and SubjectKeyId will be set by the CAS.
|
||||||
newCert := &x509.Certificate{
|
newCert := &x509.Certificate{
|
||||||
Issuer: a.x509Issuer.Subject,
|
|
||||||
Subject: oldCert.Subject,
|
Subject: oldCert.Subject,
|
||||||
NotBefore: now.Add(-1 * backdate),
|
|
||||||
NotAfter: now.Add(duration - backdate),
|
|
||||||
KeyUsage: oldCert.KeyUsage,
|
KeyUsage: oldCert.KeyUsage,
|
||||||
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
||||||
ExtKeyUsage: oldCert.ExtKeyUsage,
|
ExtKeyUsage: oldCert.ExtKeyUsage,
|
||||||
|
@ -241,10 +232,14 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy all extensions except:
|
// Copy all extensions except:
|
||||||
// 1. Authority Key Identifier - This one might be different if we rotate the intermediate certificate
|
//
|
||||||
// and it will cause a TLS bad certificate error.
|
// 1. Authority Key Identifier - This one might be different if we rotate
|
||||||
// 2. Subject Key Identifier, if rekey - For rekey, SubjectKeyIdentifier extension will be calculated
|
// the intermediate certificate and it will cause a TLS bad certificate
|
||||||
// for the new public key by NewLeafProfilewithTemplate()
|
// error.
|
||||||
|
//
|
||||||
|
// 2. Subject Key Identifier, if rekey - For rekey, SubjectKeyIdentifier
|
||||||
|
// extension will be calculated for the new public key by
|
||||||
|
// x509util.CreateCertificate()
|
||||||
for _, ext := range oldCert.Extensions {
|
for _, ext := range oldCert.Extensions {
|
||||||
if ext.Id.Equal(oidAuthorityKeyIdentifier) {
|
if ext.Id.Equal(oidAuthorityKeyIdentifier) {
|
||||||
continue
|
continue
|
||||||
|
@ -256,18 +251,22 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
||||||
newCert.ExtraExtensions = append(newCert.ExtraExtensions, ext)
|
newCert.ExtraExtensions = append(newCert.ExtraExtensions, ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
serverCert, err := x509util.CreateCertificate(newCert, a.x509Issuer, newCert.PublicKey, a.x509Signer)
|
resp, err := a.x509CAService.RenewCertificate(&casapi.RenewCertificateRequest{
|
||||||
|
Template: newCert,
|
||||||
|
Lifetime: lifetime,
|
||||||
|
Backdate: backdate,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Rekey", opts...)
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Rekey", opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.db.StoreCertificate(serverCert); err != nil {
|
if err = a.db.StoreCertificate(resp.Certificate); err != nil {
|
||||||
if err != db.ErrNotImplemented {
|
if err != db.ErrNotImplemented {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Rekey; error storing certificate in db", opts...)
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Rekey; error storing certificate in db", opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*x509.Certificate{serverCert, a.x509Issuer}, nil
|
return append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeOptions are the options for the Revoke API.
|
// RevokeOptions are the options for the Revoke API.
|
||||||
|
@ -403,30 +402,36 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
|
||||||
certTpl.NotBefore = now.Add(-1 * time.Minute)
|
certTpl.NotBefore = now.Add(-1 * time.Minute)
|
||||||
certTpl.NotAfter = now.Add(24 * time.Hour)
|
certTpl.NotAfter = now.Add(24 * time.Hour)
|
||||||
|
|
||||||
cert, err := x509util.CreateCertificate(certTpl, a.x509Issuer, cr.PublicKey, a.x509Signer)
|
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
||||||
|
Template: certTpl,
|
||||||
|
Lifetime: 24 * time.Hour,
|
||||||
|
Backdate: 1 * time.Minute,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fatal(err)
|
return fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate PEM blocks to create tls.Certificate
|
// Generate PEM blocks to create tls.Certificate
|
||||||
crtPEM := pem.EncodeToMemory(&pem.Block{
|
pemBlocks := pem.EncodeToMemory(&pem.Block{
|
||||||
Type: "CERTIFICATE",
|
Type: "CERTIFICATE",
|
||||||
Bytes: cert.Raw,
|
Bytes: resp.Certificate.Raw,
|
||||||
})
|
})
|
||||||
intermediatePEM, err := pemutil.Serialize(a.x509Issuer)
|
for _, crt := range resp.CertificateChain {
|
||||||
if err != nil {
|
pemBlocks = append(pemBlocks, pem.EncodeToMemory(&pem.Block{
|
||||||
return fatal(err)
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: crt.Raw,
|
||||||
|
})...)
|
||||||
}
|
}
|
||||||
keyPEM, err := pemutil.Serialize(priv)
|
keyPEM, err := pemutil.Serialize(priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fatal(err)
|
return fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsCrt, err := tls.X509KeyPair(append(crtPEM, pem.EncodeToMemory(intermediatePEM)...), pem.EncodeToMemory(keyPEM))
|
tlsCrt, err := tls.X509KeyPair(pemBlocks, pem.EncodeToMemory(keyPEM))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fatal(err)
|
return fatal(err)
|
||||||
}
|
}
|
||||||
// Set leaf certificate
|
// Set leaf certificate
|
||||||
tlsCrt.Leaf = cert
|
tlsCrt.Leaf = resp.Certificate
|
||||||
return &tlsCrt, nil
|
return &tlsCrt, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,11 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidStepRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
|
||||||
|
oidStepCertificateAuthority = append(asn1.ObjectIdentifier(nil), append(oidStepRoot, 2)...)
|
||||||
|
)
|
||||||
|
|
||||||
// CertificateAuthorityExtension is type used to encode the certificate
|
// CertificateAuthorityExtension is type used to encode the certificate
|
||||||
// authority extension.
|
// authority extension.
|
||||||
type CertificateAuthorityExtension struct {
|
type CertificateAuthorityExtension struct {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package apiv1
|
package apiv1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -14,27 +15,36 @@ type Options struct {
|
||||||
|
|
||||||
// Path to the credentials file used in CloudCAS
|
// Path to the credentials file used in CloudCAS
|
||||||
CredentialsFile string `json:"credentialsFile"`
|
CredentialsFile string `json:"credentialsFile"`
|
||||||
|
|
||||||
|
// CertificateAuthority reference. In CloudCAS the format is
|
||||||
|
// `projects/*/locations/*/certificateAuthorities/*`.
|
||||||
|
Certificateauthority string `json:"certificateAuthority"`
|
||||||
|
|
||||||
|
// 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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the fields in Options.
|
// Validate checks the fields in Options.
|
||||||
func (o *Options) Validate() error {
|
func (o *Options) Validate() error {
|
||||||
|
var typ Type
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return nil
|
typ = Type(SoftCAS)
|
||||||
|
} else {
|
||||||
|
typ = Type(o.Type)
|
||||||
}
|
}
|
||||||
|
// Check that the type can be loaded.
|
||||||
switch Type(strings.ToLower(o.Type)) {
|
if _, ok := LoadCertificateAuthorityServiceNewFunc(typ); !ok {
|
||||||
case DefaultCAS, SoftCAS, CloudCAS:
|
return errors.Errorf("unsupported cas type %s", typ)
|
||||||
default:
|
|
||||||
return errors.Errorf("unsupported kms type %s", o.Type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasType returns if the options have the given type.
|
// HasType returns if the options have the given type.
|
||||||
func (o *Options) HasType(t Type) bool {
|
func (o *Options) HasType(t Type) bool {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return SoftCAS == t.String()
|
return t.String() == SoftCAS
|
||||||
}
|
}
|
||||||
return Type(o.Type).String() == t.String()
|
return Type(o.Type).String() == t.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var registry = new(sync.Map)
|
var (
|
||||||
|
registry = new(sync.Map)
|
||||||
|
)
|
||||||
|
|
||||||
// CertificateAuthorityServiceNewFunc is the type that represents the method to initialize a new
|
// CertificateAuthorityServiceNewFunc is the type that represents the method to initialize a new
|
||||||
// CertificateAuthorityService.
|
// CertificateAuthorityService.
|
||||||
|
@ -13,12 +15,12 @@ type CertificateAuthorityServiceNewFunc func(ctx context.Context, opts Options)
|
||||||
|
|
||||||
// Register adds to the registry a method to create a KeyManager of type t.
|
// Register adds to the registry a method to create a KeyManager of type t.
|
||||||
func Register(t Type, fn CertificateAuthorityServiceNewFunc) {
|
func Register(t Type, fn CertificateAuthorityServiceNewFunc) {
|
||||||
registry.Store(t, fn)
|
registry.Store(t.String(), fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadCertificateAuthorityServiceNewFunc returns the function initialize a KayManager.
|
// LoadCertificateAuthorityServiceNewFunc returns the function initialize a KayManager.
|
||||||
func LoadCertificateAuthorityServiceNewFunc(t Type) (CertificateAuthorityServiceNewFunc, bool) {
|
func LoadCertificateAuthorityServiceNewFunc(t Type) (CertificateAuthorityServiceNewFunc, bool) {
|
||||||
v, ok := registry.Load(t)
|
v, ok := registry.Load(t.String())
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
package apiv1
|
package apiv1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateCertificateRequest struct {
|
type CreateCertificateRequest struct {
|
||||||
Template *x509.Certificate
|
Template *x509.Certificate
|
||||||
Issuer *x509.Certificate
|
|
||||||
Signer crypto.Signer
|
|
||||||
Lifetime time.Duration
|
Lifetime time.Duration
|
||||||
Backdate time.Duration
|
Backdate time.Duration
|
||||||
RequestID string
|
RequestID string
|
||||||
|
@ -21,8 +18,6 @@ type CreateCertificateResponse struct {
|
||||||
|
|
||||||
type RenewCertificateRequest struct {
|
type RenewCertificateRequest struct {
|
||||||
Template *x509.Certificate
|
Template *x509.Certificate
|
||||||
Issuer *x509.Certificate
|
|
||||||
Signer crypto.Signer
|
|
||||||
Lifetime time.Duration
|
Lifetime time.Duration
|
||||||
Backdate time.Duration
|
Backdate time.Duration
|
||||||
RequestID string
|
RequestID string
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
package apiv1
|
package apiv1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/asn1"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
oidStepRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
|
|
||||||
oidStepCertificateAuthority = append(asn1.ObjectIdentifier(nil), append(oidStepRoot, 2)...)
|
|
||||||
)
|
|
||||||
|
|
||||||
// CertificateAuthorityService is the interface implemented to support external
|
// CertificateAuthorityService is the interface implemented to support external
|
||||||
// certificate authorities.
|
// certificate authorities.
|
||||||
type CertificateAuthorityService interface {
|
type CertificateAuthorityService interface {
|
||||||
|
@ -18,27 +12,24 @@ type CertificateAuthorityService interface {
|
||||||
RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error)
|
RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type represents the KMS type used.
|
// Type represents the CAS type used.
|
||||||
type Type string
|
type Type string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultCAS is a CertificateAuthorityService using software.
|
// DefaultCAS is a CertificateAuthorityService using software.
|
||||||
DefaultCAS = ""
|
DefaultCAS = ""
|
||||||
// SoftCAS is a CertificateAuthorityService using software.
|
// SoftCAS is a CertificateAuthorityService using software.
|
||||||
SoftCAS = "SoftCAS"
|
SoftCAS = "softcas"
|
||||||
// CloudCAS is a CertificateAuthorityService using Google Cloud CAS.
|
// CloudCAS is a CertificateAuthorityService using Google Cloud CAS.
|
||||||
CloudCAS = "CloudCAS"
|
CloudCAS = "cloudcas"
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the given type as a string. All the letters will be lowercase.
|
// String returns a string from the type. It will always return the lower case
|
||||||
|
// version of the Type, as we need a standard type to compare and use as the
|
||||||
|
// registry key.
|
||||||
func (t Type) String() string {
|
func (t Type) String() string {
|
||||||
if t == "" {
|
if t == "" {
|
||||||
return SoftCAS
|
return SoftCAS
|
||||||
}
|
}
|
||||||
for _, s := range []string{SoftCAS, CloudCAS} {
|
return strings.ToLower(string(t))
|
||||||
if strings.EqualFold(s, string(t)) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(t)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ type caClient interface{}
|
||||||
// New creates a new CertificateAuthorityService implementation using Google
|
// New creates a new CertificateAuthorityService implementation using Google
|
||||||
// Cloud CAS.
|
// Cloud CAS.
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
||||||
|
if opts.Certificateauthority == "" {
|
||||||
|
return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
var cloudOpts []option.ClientOption
|
var cloudOpts []option.ClientOption
|
||||||
if opts.CredentialsFile != "" {
|
if opts.CredentialsFile != "" {
|
||||||
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
|
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
|
||||||
|
@ -57,7 +61,7 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
||||||
|
|
||||||
return &CloudCAS{
|
return &CloudCAS{
|
||||||
client: client,
|
client: client,
|
||||||
certificateAuthority: "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/Smallstep-Test-Intermediate-CA",
|
certificateAuthority: opts.Certificateauthority,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +91,9 @@ func (c *CloudCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv
|
||||||
func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
||||||
switch {
|
switch {
|
||||||
case req.Template == nil:
|
case req.Template == nil:
|
||||||
return nil, errors.New("renewCertificate `template` cannot be nil")
|
return nil, errors.New("renewCertificateRequest `template` cannot be nil")
|
||||||
case req.Lifetime == 0:
|
case req.Lifetime == 0:
|
||||||
return nil, errors.New("renewCertificate `lifetime` cannot be 0")
|
return nil, errors.New("renewCertificateRequest `lifetime` cannot be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, chain, err := c.createCertificate(req.Template, req.Lifetime, req.RequestID)
|
cert, chain, err := c.createCertificate(req.Template, req.Lifetime, req.RequestID)
|
||||||
|
@ -106,7 +110,7 @@ func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.
|
||||||
// RevokeCertificate a certificate using Google Cloud CAS.
|
// RevokeCertificate a certificate using Google Cloud CAS.
|
||||||
func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||||
if req.Certificate == nil {
|
if req.Certificate == nil {
|
||||||
return nil, errors.New("revokeCertificate `certificate` cannot be nil")
|
return nil, errors.New("revokeCertificateRequest `certificate` cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
ext, ok := apiv1.FindCertificateAuthorityExtension(req.Certificate)
|
ext, ok := apiv1.FindCertificateAuthorityExtension(req.Certificate)
|
||||||
|
|
|
@ -2,8 +2,11 @@ package softcas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
|
@ -15,19 +18,47 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SoftCAS implements a Certificate Authority Service using Golang crypto.
|
var now = func() time.Time {
|
||||||
// This is the default CAS used in step-ca.
|
return time.Now()
|
||||||
type SoftCAS struct{}
|
|
||||||
|
|
||||||
// New creates a new CertificateAuthorityService implementation using Golang
|
|
||||||
// crypto.
|
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) {
|
|
||||||
return &SoftCAS{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCertificate signs a new certificate using Golang crypto.
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new CertificateAuthorityService implementation using Golang or KMS
|
||||||
|
// crypto.
|
||||||
|
func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) {
|
||||||
|
switch {
|
||||||
|
case opts.Issuer == nil:
|
||||||
|
return nil, errors.New("softCAS 'issuer' cannot be nil")
|
||||||
|
case opts.Signer == nil:
|
||||||
|
return nil, errors.New("softCAS 'signer' cannot be nil")
|
||||||
|
}
|
||||||
|
return &SoftCAS{
|
||||||
|
Issuer: opts.Issuer,
|
||||||
|
Signer: opts.Signer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCertificate signs a new certificate using Golang or KMS crypto.
|
||||||
func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
|
func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
|
||||||
cert, err := x509util.CreateCertificate(req.Template, req.Issuer, req.Template.PublicKey, req.Signer)
|
switch {
|
||||||
|
case req.Template == nil:
|
||||||
|
return nil, errors.New("createCertificateRequest `template` cannot be nil")
|
||||||
|
case req.Lifetime == 0:
|
||||||
|
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := now()
|
||||||
|
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
||||||
|
req.Template.NotAfter = t.Add(req.Lifetime)
|
||||||
|
req.Template.Issuer = c.Issuer.Subject
|
||||||
|
|
||||||
|
cert, err := x509util.CreateCertificate(req.Template, c.Issuer, req.Template.PublicKey, c.Signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -35,13 +66,36 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
|
||||||
return &apiv1.CreateCertificateResponse{
|
return &apiv1.CreateCertificateResponse{
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
CertificateChain: []*x509.Certificate{
|
CertificateChain: []*x509.Certificate{
|
||||||
req.Issuer,
|
c.Issuer,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenewCertificate signs the given certificate template using Golang or KMS crypto.
|
||||||
func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
switch {
|
||||||
|
case req.Template == nil:
|
||||||
|
return nil, errors.New("createCertificateRequest `template` cannot be nil")
|
||||||
|
case req.Lifetime == 0:
|
||||||
|
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := now()
|
||||||
|
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
||||||
|
req.Template.NotAfter = t.Add(req.Lifetime)
|
||||||
|
req.Template.Issuer = c.Issuer.Subject
|
||||||
|
|
||||||
|
cert, err := x509util.CreateCertificate(req.Template, c.Issuer, req.Template.PublicKey, c.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiv1.RenewCertificateResponse{
|
||||||
|
Certificate: cert,
|
||||||
|
CertificateChain: []*x509.Certificate{
|
||||||
|
c.Issuer,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCertificate revokes the given certificate in step-ca.
|
// RevokeCertificate revokes the given certificate in step-ca.
|
||||||
|
|
Loading…
Add table
Reference in a new issue