Add authority.WithX509SignerFunc
This change adds a new authority option that allows to pass a callback that returns the certificate chain and signer used to sign X.509 certificates. This option will be used by Caddy, they renew the intermediate certificate weekly and there's no other way to replace it without re-creating the embedded CA. Fixes #874
This commit is contained in:
parent
49de04661b
commit
955d4cf80d
4 changed files with 194 additions and 46 deletions
|
@ -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.
|
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
|
||||||
func WithSSHUserSigner(s crypto.Signer) Option {
|
func WithSSHUserSigner(s crypto.Signer) Option {
|
||||||
return func(a *Authority) error {
|
return func(a *Authority) error {
|
||||||
|
|
|
@ -31,13 +31,18 @@ type Options struct {
|
||||||
// https://cloud.google.com/docs/authentication.
|
// https://cloud.google.com/docs/authentication.
|
||||||
CredentialsFile string `json:"credentialsFile,omitempty"`
|
CredentialsFile string `json:"credentialsFile,omitempty"`
|
||||||
|
|
||||||
// Certificate and signer are the issuer certificate, along with any other
|
// CertificateChain and Signer are the issuer certificate, along with any
|
||||||
// bundled certificates to be returned in the chain for consumers, and
|
// 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
|
// signer used in SoftCAS. They are configured in ca.json crt and key
|
||||||
// properties.
|
// properties.
|
||||||
CertificateChain []*x509.Certificate `json:"-"`
|
CertificateChain []*x509.Certificate `json:"-"`
|
||||||
Signer crypto.Signer `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
|
// IsCreator is set to true when we're creating a certificate authority. It
|
||||||
// is used to skip some validations when initializing a
|
// is used to skip some validations when initializing a
|
||||||
// CertificateAuthority. This option is used on SoftCAS and CloudCAS.
|
// CertificateAuthority. This option is used on SoftCAS and CloudCAS.
|
||||||
|
|
|
@ -26,6 +26,7 @@ var now = time.Now
|
||||||
type SoftCAS struct {
|
type SoftCAS struct {
|
||||||
CertificateChain []*x509.Certificate
|
CertificateChain []*x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
|
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
|
||||||
KeyManager kms.KeyManager
|
KeyManager kms.KeyManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,15 +35,16 @@ type SoftCAS struct {
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) {
|
func New(ctx context.Context, opts apiv1.Options) (*SoftCAS, error) {
|
||||||
if !opts.IsCreator {
|
if !opts.IsCreator {
|
||||||
switch {
|
switch {
|
||||||
case len(opts.CertificateChain) == 0:
|
case len(opts.CertificateChain) == 0 && opts.CertificateSigner == nil:
|
||||||
return nil, errors.New("softCAS 'CertificateChain' cannot be 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 nil, errors.New("softCAS 'signer' cannot be nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &SoftCAS{
|
return &SoftCAS{
|
||||||
CertificateChain: opts.CertificateChain,
|
CertificateChain: opts.CertificateChain,
|
||||||
Signer: opts.Signer,
|
Signer: opts.Signer,
|
||||||
|
CertificateSigner: opts.CertificateSigner,
|
||||||
KeyManager: opts.KeyManager,
|
KeyManager: opts.KeyManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -57,6 +59,7 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
|
||||||
}
|
}
|
||||||
|
|
||||||
t := now()
|
t := now()
|
||||||
|
|
||||||
// Provisioners can also set specific values.
|
// Provisioners can also set specific values.
|
||||||
if req.Template.NotBefore.IsZero() {
|
if req.Template.NotBefore.IsZero() {
|
||||||
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
||||||
|
@ -64,16 +67,21 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
|
||||||
if req.Template.NotAfter.IsZero() {
|
if req.Template.NotAfter.IsZero() {
|
||||||
req.Template.NotAfter = t.Add(req.Lifetime)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &apiv1.CreateCertificateResponse{
|
return &apiv1.CreateCertificateResponse{
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
CertificateChain: c.CertificateChain,
|
CertificateChain: chain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,16 +97,21 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R
|
||||||
t := now()
|
t := now()
|
||||||
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
req.Template.NotBefore = t.Add(-1 * req.Backdate)
|
||||||
req.Template.NotAfter = t.Add(req.Lifetime)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &apiv1.RenewCertificateResponse{
|
return &apiv1.RenewCertificateResponse{
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
CertificateChain: c.CertificateChain,
|
CertificateChain: chain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,9 +119,13 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R
|
||||||
// operation is a no-op as the actual revoke will happen when we store the entry
|
// operation is a no-op as the actual revoke will happen when we store the entry
|
||||||
// in the db.
|
// in the db.
|
||||||
func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||||
|
chain, _, err := c.getCertSigner()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &apiv1.RevokeCertificateResponse{
|
return &apiv1.RevokeCertificateResponse{
|
||||||
Certificate: req.Certificate,
|
Certificate: req.Certificate,
|
||||||
CertificateChain: c.CertificateChain,
|
CertificateChain: chain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +196,7 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori
|
||||||
}, nil
|
}, 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) {
|
func (c *SoftCAS) initializeKeyManager() (err error) {
|
||||||
if c.KeyManager == nil {
|
if c.KeyManager == nil {
|
||||||
c.KeyManager, err = kms.New(context.Background(), kmsapi.Options{
|
c.KeyManager, err = kms.New(context.Background(), kmsapi.Options{
|
||||||
|
@ -189,6 +206,15 @@ func (c *SoftCAS) initializeKeyManager() (err error) {
|
||||||
return
|
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.
|
// createKey uses the configured kms to create a key.
|
||||||
func (c *SoftCAS) createKey(req *kmsapi.CreateKeyRequest) (*kmsapi.CreateKeyResponse, error) {
|
func (c *SoftCAS) createKey(req *kmsapi.CreateKeyRequest) (*kmsapi.CreateKeyResponse, error) {
|
||||||
if err := c.initializeKeyManager(); err != nil {
|
if err := c.initializeKeyManager(); err != nil {
|
||||||
|
|
|
@ -73,6 +73,12 @@ var (
|
||||||
testSignedTemplate = mustSign(testTemplate, testIssuer, testNow, testNow.Add(24*time.Hour))
|
testSignedTemplate = mustSign(testTemplate, testIssuer, testNow, testNow.Add(24*time.Hour))
|
||||||
testSignedRootTemplate = mustSign(testRootTemplate, testRootTemplate, testNow, testNow.Add(24*time.Hour))
|
testSignedRootTemplate = mustSign(testRootTemplate, testRootTemplate, testNow, testNow.Add(24*time.Hour))
|
||||||
testSignedIntermediateTemplate = mustSign(testIntermediateTemplate, testSignedRootTemplate, testNow, testNow.Add(24*time.Hour))
|
testSignedIntermediateTemplate = mustSign(testIntermediateTemplate, testSignedRootTemplate, testNow, testNow.Add(24*time.Hour))
|
||||||
|
testCertificateSigner = func() ([]*x509.Certificate, crypto.Signer, error) {
|
||||||
|
return []*x509.Certificate{testIssuer}, testSigner, nil
|
||||||
|
}
|
||||||
|
testFailCertificateSigner = func() ([]*x509.Certificate, crypto.Signer, error) {
|
||||||
|
return nil, nil, errTest
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type signatureAlgorithmSigner struct {
|
type signatureAlgorithmSigner struct {
|
||||||
|
@ -186,6 +192,10 @@ func setTeeReader(t *testing.T, w *bytes.Buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
|
assertEqual := func(x, y interface{}) bool {
|
||||||
|
return reflect.DeepEqual(x, y) || fmt.Sprintf("%#v", x) == fmt.Sprintf("%#v", y)
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
opts apiv1.Options
|
opts apiv1.Options
|
||||||
|
@ -197,6 +207,7 @@ func TestNew(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}}, &SoftCAS{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}, false},
|
{"ok", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}}, &SoftCAS{CertificateChain: []*x509.Certificate{testIssuer}, Signer: testSigner}, false},
|
||||||
|
{"ok with callback", args{context.Background(), apiv1.Options{CertificateSigner: testCertificateSigner}}, &SoftCAS{CertificateSigner: testCertificateSigner}, false},
|
||||||
{"fail no issuer", args{context.Background(), apiv1.Options{Signer: testSigner}}, nil, true},
|
{"fail no issuer", args{context.Background(), apiv1.Options{Signer: testSigner}}, nil, true},
|
||||||
{"fail no signer", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}}}, nil, true},
|
{"fail no signer", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}}}, nil, true},
|
||||||
}
|
}
|
||||||
|
@ -207,7 +218,7 @@ func TestNew(t *testing.T) {
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !assertEqual(got, tt.want) {
|
||||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
t.Errorf("New() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -267,6 +278,7 @@ func TestSoftCAS_CreateCertificate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
|
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
req *apiv1.CreateCertificateRequest
|
req *apiv1.CreateCertificateRequest
|
||||||
|
@ -278,36 +290,45 @@ func TestSoftCAS_CreateCertificate(t *testing.T) {
|
||||||
want *apiv1.CreateCertificateResponse
|
want *apiv1.CreateCertificateResponse
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{
|
{"ok", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
|
||||||
Template: testTemplate, Lifetime: 24 * time.Hour,
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.CreateCertificateResponse{
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok signature algorithm", fields{testIssuer, saSigner}, args{&apiv1.CreateCertificateRequest{
|
{"ok signature algorithm", fields{testIssuer, saSigner, nil}, args{&apiv1.CreateCertificateRequest{
|
||||||
Template: &saTemplate, Lifetime: 24 * time.Hour,
|
Template: &saTemplate, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.CreateCertificateResponse{
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok with notBefore", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{
|
{"ok with notBefore", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
|
||||||
Template: &tmplNotBefore, Lifetime: 24 * time.Hour,
|
Template: &tmplNotBefore, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.CreateCertificateResponse{
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok with notBefore+notAfter", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{
|
{"ok with notBefore+notAfter", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
|
||||||
Template: &tmplWithLifetime, Lifetime: 24 * time.Hour,
|
Template: &tmplWithLifetime, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.CreateCertificateResponse{
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"fail template", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{Lifetime: 24 * time.Hour}}, nil, true},
|
{"ok with callback", fields{nil, nil, testCertificateSigner}, args{&apiv1.CreateCertificateRequest{
|
||||||
{"fail lifetime", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{Template: testTemplate}}, nil, true},
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
{"fail CreateCertificate", fields{testIssuer, testSigner}, args{&apiv1.CreateCertificateRequest{
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
|
Certificate: testSignedTemplate,
|
||||||
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
|
}, false},
|
||||||
|
{"fail template", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{Lifetime: 24 * time.Hour}}, nil, true},
|
||||||
|
{"fail lifetime", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{Template: testTemplate}}, nil, true},
|
||||||
|
{"fail CreateCertificate", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
|
||||||
Template: &tmplNoSerial,
|
Template: &tmplNoSerial,
|
||||||
Lifetime: 24 * time.Hour,
|
Lifetime: 24 * time.Hour,
|
||||||
}}, nil, true},
|
}}, nil, true},
|
||||||
|
{"fail with callback", fields{nil, nil, testFailCertificateSigner}, args{&apiv1.CreateCertificateRequest{
|
||||||
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
|
}}, nil, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -315,6 +336,7 @@ func TestSoftCAS_CreateCertificate(t *testing.T) {
|
||||||
c := &SoftCAS{
|
c := &SoftCAS{
|
||||||
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
||||||
Signer: tt.fields.Signer,
|
Signer: tt.fields.Signer,
|
||||||
|
CertificateSigner: tt.fields.CertificateSigner,
|
||||||
}
|
}
|
||||||
got, err := c.CreateCertificate(tt.args.req)
|
got, err := c.CreateCertificate(tt.args.req)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
@ -347,6 +369,7 @@ func TestSoftCAS_RenewCertificate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
|
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
req *apiv1.RenewCertificateRequest
|
req *apiv1.RenewCertificateRequest
|
||||||
|
@ -358,30 +381,40 @@ func TestSoftCAS_RenewCertificate(t *testing.T) {
|
||||||
want *apiv1.RenewCertificateResponse
|
want *apiv1.RenewCertificateResponse
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{
|
{"ok", fields{testIssuer, testSigner, nil}, args{&apiv1.RenewCertificateRequest{
|
||||||
Template: testTemplate, Lifetime: 24 * time.Hour,
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.RenewCertificateResponse{
|
}}, &apiv1.RenewCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok signature algorithm", fields{testIssuer, saSigner}, args{&apiv1.RenewCertificateRequest{
|
{"ok signature algorithm", fields{testIssuer, saSigner, nil}, args{&apiv1.RenewCertificateRequest{
|
||||||
Template: testTemplate, Lifetime: 24 * time.Hour,
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
}}, &apiv1.RenewCertificateResponse{
|
}}, &apiv1.RenewCertificateResponse{
|
||||||
Certificate: testSignedTemplate,
|
Certificate: testSignedTemplate,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"fail template", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{Lifetime: 24 * time.Hour}}, nil, true},
|
{"ok with callback", fields{nil, nil, testCertificateSigner}, args{&apiv1.RenewCertificateRequest{
|
||||||
{"fail lifetime", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{Template: testTemplate}}, nil, true},
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
{"fail CreateCertificate", fields{testIssuer, testSigner}, args{&apiv1.RenewCertificateRequest{
|
}}, &apiv1.RenewCertificateResponse{
|
||||||
|
Certificate: testSignedTemplate,
|
||||||
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
|
}, false},
|
||||||
|
{"fail template", fields{testIssuer, testSigner, nil}, args{&apiv1.RenewCertificateRequest{Lifetime: 24 * time.Hour}}, nil, true},
|
||||||
|
{"fail lifetime", fields{testIssuer, testSigner, nil}, args{&apiv1.RenewCertificateRequest{Template: testTemplate}}, nil, true},
|
||||||
|
{"fail CreateCertificate", fields{testIssuer, testSigner, nil}, args{&apiv1.RenewCertificateRequest{
|
||||||
Template: &tmplNoSerial,
|
Template: &tmplNoSerial,
|
||||||
Lifetime: 24 * time.Hour,
|
Lifetime: 24 * time.Hour,
|
||||||
}}, nil, true},
|
}}, nil, true},
|
||||||
|
{"fail with callback", fields{nil, nil, testFailCertificateSigner}, args{&apiv1.RenewCertificateRequest{
|
||||||
|
Template: testTemplate, Lifetime: 24 * time.Hour,
|
||||||
|
}}, nil, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &SoftCAS{
|
c := &SoftCAS{
|
||||||
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
||||||
Signer: tt.fields.Signer,
|
Signer: tt.fields.Signer,
|
||||||
|
CertificateSigner: tt.fields.CertificateSigner,
|
||||||
}
|
}
|
||||||
got, err := c.RenewCertificate(tt.args.req)
|
got, err := c.RenewCertificate(tt.args.req)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
@ -399,6 +432,7 @@ func TestSoftCAS_RevokeCertificate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
|
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
req *apiv1.RevokeCertificateRequest
|
req *apiv1.RevokeCertificateRequest
|
||||||
|
@ -410,7 +444,7 @@ func TestSoftCAS_RevokeCertificate(t *testing.T) {
|
||||||
want *apiv1.RevokeCertificateResponse
|
want *apiv1.RevokeCertificateResponse
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", fields{testIssuer, testSigner}, args{&apiv1.RevokeCertificateRequest{
|
{"ok", fields{testIssuer, testSigner, nil}, args{&apiv1.RevokeCertificateRequest{
|
||||||
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
||||||
Reason: "test reason",
|
Reason: "test reason",
|
||||||
ReasonCode: 1,
|
ReasonCode: 1,
|
||||||
|
@ -418,23 +452,37 @@ func TestSoftCAS_RevokeCertificate(t *testing.T) {
|
||||||
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok no cert", fields{testIssuer, testSigner}, args{&apiv1.RevokeCertificateRequest{
|
{"ok no cert", fields{testIssuer, testSigner, nil}, args{&apiv1.RevokeCertificateRequest{
|
||||||
Reason: "test reason",
|
Reason: "test reason",
|
||||||
ReasonCode: 1,
|
ReasonCode: 1,
|
||||||
}}, &apiv1.RevokeCertificateResponse{
|
}}, &apiv1.RevokeCertificateResponse{
|
||||||
Certificate: nil,
|
Certificate: nil,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
{"ok empty", fields{testIssuer, testSigner}, args{&apiv1.RevokeCertificateRequest{}}, &apiv1.RevokeCertificateResponse{
|
{"ok empty", fields{testIssuer, testSigner, nil}, args{&apiv1.RevokeCertificateRequest{}}, &apiv1.RevokeCertificateResponse{
|
||||||
Certificate: nil,
|
Certificate: nil,
|
||||||
CertificateChain: []*x509.Certificate{testIssuer},
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
}, false},
|
}, false},
|
||||||
|
{"ok with callback", fields{nil, nil, testCertificateSigner}, args{&apiv1.RevokeCertificateRequest{
|
||||||
|
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
||||||
|
Reason: "test reason",
|
||||||
|
ReasonCode: 1,
|
||||||
|
}}, &apiv1.RevokeCertificateResponse{
|
||||||
|
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
||||||
|
CertificateChain: []*x509.Certificate{testIssuer},
|
||||||
|
}, false},
|
||||||
|
{"fail with callback", fields{nil, nil, testFailCertificateSigner}, args{&apiv1.RevokeCertificateRequest{
|
||||||
|
Certificate: &x509.Certificate{Subject: pkix.Name{CommonName: "fake"}},
|
||||||
|
Reason: "test reason",
|
||||||
|
ReasonCode: 1,
|
||||||
|
}}, nil, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := &SoftCAS{
|
c := &SoftCAS{
|
||||||
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
|
||||||
Signer: tt.fields.Signer,
|
Signer: tt.fields.Signer,
|
||||||
|
CertificateSigner: tt.fields.CertificateSigner,
|
||||||
}
|
}
|
||||||
got, err := c.RevokeCertificate(tt.args.req)
|
got, err := c.RevokeCertificate(tt.args.req)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
@ -609,3 +657,56 @@ func TestSoftCAS_CreateCertificateAuthority(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSoftCAS_defaultKeyManager(t *testing.T) {
|
||||||
|
mockNow(t)
|
||||||
|
type args struct {
|
||||||
|
req *apiv1.CreateCertificateAuthorityRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok root", args{&apiv1.CreateCertificateAuthorityRequest{
|
||||||
|
Type: apiv1.RootCA,
|
||||||
|
Template: &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Test Root CA"},
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
MaxPathLen: 1,
|
||||||
|
SerialNumber: big.NewInt(1234),
|
||||||
|
},
|
||||||
|
Lifetime: 24 * time.Hour,
|
||||||
|
}}, false},
|
||||||
|
{"ok intermediate", args{&apiv1.CreateCertificateAuthorityRequest{
|
||||||
|
Type: apiv1.IntermediateCA,
|
||||||
|
Template: testIntermediateTemplate,
|
||||||
|
Lifetime: 24 * time.Hour,
|
||||||
|
Parent: &apiv1.CreateCertificateAuthorityResponse{
|
||||||
|
Certificate: testSignedRootTemplate,
|
||||||
|
Signer: testSigner,
|
||||||
|
},
|
||||||
|
}}, false},
|
||||||
|
{"fail with default key manager", args{&apiv1.CreateCertificateAuthorityRequest{
|
||||||
|
Type: apiv1.IntermediateCA,
|
||||||
|
Template: testIntermediateTemplate,
|
||||||
|
Lifetime: 24 * time.Hour,
|
||||||
|
Parent: &apiv1.CreateCertificateAuthorityResponse{
|
||||||
|
Certificate: testSignedRootTemplate,
|
||||||
|
Signer: &badSigner{},
|
||||||
|
},
|
||||||
|
}}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &SoftCAS{}
|
||||||
|
_, err := c.CreateCertificateAuthority(tt.args.req)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("SoftCAS.CreateCertificateAuthority() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue