smallstep-certificates/cas/softcas/softcas_test.go
Mariano Cano 955d4cf80d 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
2022-03-28 17:54:35 -07:00

713 lines
23 KiB
Go

package softcas
import (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"io"
"math/big"
"reflect"
"testing"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/cas/apiv1"
"github.com/smallstep/certificates/kms"
kmsapi "github.com/smallstep/certificates/kms/apiv1"
"go.step.sm/crypto/pemutil"
"go.step.sm/crypto/x509util"
)
var (
testIntermediatePem = `-----BEGIN CERTIFICATE-----
MIIBPjCB8aADAgECAhAk4aPIlsVvQg3gveApc3mIMAUGAytlcDAeMRwwGgYDVQQD
ExNTbWFsbHN0ZXAgVW5pdCBUZXN0MB4XDTIwMDkxNjAyMDgwMloXDTMwMDkxNDAy
MDgwMlowHjEcMBoGA1UEAxMTU21hbGxzdGVwIFVuaXQgVGVzdDAqMAUGAytlcAMh
ANLs3JCzECR29biut0NDsaLnh0BGij5eJx6VkdJPfS/ko0UwQzAOBgNVHQ8BAf8E
BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUup5qpZFMAFdgK7RB
xNzmUaQM8YwwBQYDK2VwA0EAAwcW25E/6bchyKwp3RRK1GXiPMDCc+hsTJxuOLWy
YM7ga829dU8X4pRcEEAcBndqCED/502excjEK7U9vCkFCg==
-----END CERTIFICATE-----`
testIntermediateKeyPem = `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEII9ZckcrDKlbhZKR0jp820Uz6mOMLFsq2JhI+Tl7WJwH
-----END PRIVATE KEY-----`
)
var (
errTest = errors.New("test error")
testIssuer = mustIssuer()
testSigner = mustSigner()
testTemplate = &x509.Certificate{
Subject: pkix.Name{CommonName: "test.smallstep.com"},
DNSNames: []string{"test.smallstep.com"},
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
PublicKey: testSigner.Public(),
SerialNumber: big.NewInt(1234),
}
testRootTemplate = &x509.Certificate{
Subject: pkix.Name{CommonName: "Test Root CA"},
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
PublicKey: testSigner.Public(),
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 1,
SerialNumber: big.NewInt(1234),
}
testIntermediateTemplate = &x509.Certificate{
Subject: pkix.Name{CommonName: "Test Intermediate CA"},
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
PublicKey: testSigner.Public(),
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 0,
MaxPathLenZero: true,
SerialNumber: big.NewInt(1234),
}
testNow = time.Now()
testSignedTemplate = mustSign(testTemplate, testIssuer, 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))
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 {
crypto.Signer
algorithm x509.SignatureAlgorithm
}
func (s *signatureAlgorithmSigner) SignatureAlgorithm() x509.SignatureAlgorithm {
return s.algorithm
}
type mockKeyManager struct {
signer crypto.Signer
errGetPublicKey error
errCreateKey error
errCreatesigner error
errClose error
}
func (m *mockKeyManager) GetPublicKey(req *kmsapi.GetPublicKeyRequest) (crypto.PublicKey, error) {
signer := testSigner
if m.signer != nil {
signer = m.signer
}
return signer.Public(), m.errGetPublicKey
}
func (m *mockKeyManager) CreateKey(req *kmsapi.CreateKeyRequest) (*kmsapi.CreateKeyResponse, error) {
signer := testSigner
if m.signer != nil {
signer = m.signer
}
return &kmsapi.CreateKeyResponse{
Name: req.Name,
PrivateKey: signer,
PublicKey: signer.Public(),
}, m.errCreateKey
}
func (m *mockKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (crypto.Signer, error) {
signer := testSigner
if m.signer != nil {
signer = m.signer
}
return signer, m.errCreatesigner
}
func (m *mockKeyManager) CreateDecrypter(req *kmsapi.CreateDecrypterRequest) (crypto.Decrypter, error) {
return nil, nil
}
func (m *mockKeyManager) Close() error {
return m.errClose
}
type badSigner struct{}
func (b *badSigner) Public() crypto.PublicKey {
return testSigner.Public()
}
func (b *badSigner) Sign(_ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) {
return nil, fmt.Errorf("💥")
}
func mockNow(t *testing.T) {
tmp := now
now = func() time.Time {
return testNow
}
t.Cleanup(func() {
now = tmp
})
}
func mustIssuer() *x509.Certificate {
v, err := pemutil.Parse([]byte(testIntermediatePem))
if err != nil {
panic(err)
}
return v.(*x509.Certificate)
}
func mustSigner() crypto.Signer {
v, err := pemutil.Parse([]byte(testIntermediateKeyPem))
if err != nil {
panic(err)
}
return v.(crypto.Signer)
}
func mustSign(template, parent *x509.Certificate, notBefore, notAfter time.Time) *x509.Certificate {
tmpl := *template
tmpl.NotBefore = notBefore
tmpl.NotAfter = notAfter
tmpl.Issuer = parent.Subject
cert, err := x509util.CreateCertificate(&tmpl, parent, tmpl.PublicKey, testSigner)
if err != nil {
panic(err)
}
return cert
}
func setTeeReader(t *testing.T, w *bytes.Buffer) {
t.Helper()
reader := rand.Reader
t.Cleanup(func() {
rand.Reader = reader
})
rand.Reader = io.TeeReader(reader, w)
}
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 {
ctx context.Context
opts apiv1.Options
}
tests := []struct {
name string
args args
want *SoftCAS
wantErr bool
}{
{"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 signer", args{context.Background(), apiv1.Options{CertificateChain: []*x509.Certificate{testIssuer}}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := New(tt.args.ctx, tt.args.opts)
if (err != nil) != tt.wantErr {
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !assertEqual(got, tt.want) {
t.Errorf("New() = %v, want %v", got, tt.want)
}
})
}
}
func TestNew_register(t *testing.T) {
newFn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.SoftCAS)
if !ok {
t.Error("apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.SoftCAS) was not found")
return
}
want := &SoftCAS{
CertificateChain: []*x509.Certificate{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
}
if !reflect.DeepEqual(got, want) {
t.Errorf("New() = %v, want %v", got, want)
}
}
func TestSoftCAS_CreateCertificate(t *testing.T) {
mockNow(t)
// Set rand.Reader to EOF
buf := new(bytes.Buffer)
setTeeReader(t, buf)
rand.Reader = buf
tmplNotBefore := *testTemplate
tmplNotBefore.NotBefore = testNow
tmplNotAfter := *testTemplate
tmplNotAfter.NotAfter = testNow.Add(24 * time.Hour)
tmplWithLifetime := *testTemplate
tmplWithLifetime.NotBefore = testNow
tmplWithLifetime.NotAfter = testNow.Add(24 * time.Hour)
tmplNoSerial := *testTemplate
tmplNoSerial.SerialNumber = nil
saTemplate := *testSignedTemplate
saTemplate.SignatureAlgorithm = 0
saSigner := &signatureAlgorithmSigner{
Signer: testSigner,
algorithm: x509.PureEd25519,
}
type fields struct {
Issuer *x509.Certificate
Signer crypto.Signer
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
}
type args struct {
req *apiv1.CreateCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.CreateCertificateResponse
wantErr bool
}{
{"ok", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok signature algorithm", fields{testIssuer, saSigner, nil}, args{&apiv1.CreateCertificateRequest{
Template: &saTemplate, Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok with notBefore", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
Template: &tmplNotBefore, Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok with notBefore+notAfter", fields{testIssuer, testSigner, nil}, args{&apiv1.CreateCertificateRequest{
Template: &tmplWithLifetime, Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok with callback", fields{nil, nil, testCertificateSigner}, args{&apiv1.CreateCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, &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,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail with callback", fields{nil, nil, testFailCertificateSigner}, args{&apiv1.CreateCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &SoftCAS{
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
Signer: tt.fields.Signer,
CertificateSigner: tt.fields.CertificateSigner,
}
got, err := c.CreateCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("SoftCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SoftCAS.CreateCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func TestSoftCAS_RenewCertificate(t *testing.T) {
mockNow(t)
// Set rand.Reader to EOF
buf := new(bytes.Buffer)
setTeeReader(t, buf)
rand.Reader = buf
tmplNoSerial := *testTemplate
tmplNoSerial.SerialNumber = nil
saSigner := &signatureAlgorithmSigner{
Signer: testSigner,
algorithm: x509.PureEd25519,
}
type fields struct {
Issuer *x509.Certificate
Signer crypto.Signer
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
}
type args struct {
req *apiv1.RenewCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.RenewCertificateResponse
wantErr bool
}{
{"ok", fields{testIssuer, testSigner, nil}, args{&apiv1.RenewCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, &apiv1.RenewCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok signature algorithm", fields{testIssuer, saSigner, nil}, args{&apiv1.RenewCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, &apiv1.RenewCertificateResponse{
Certificate: testSignedTemplate,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok with callback", fields{nil, nil, testCertificateSigner}, args{&apiv1.RenewCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, &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,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail with callback", fields{nil, nil, testFailCertificateSigner}, args{&apiv1.RenewCertificateRequest{
Template: testTemplate, Lifetime: 24 * time.Hour,
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &SoftCAS{
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
Signer: tt.fields.Signer,
CertificateSigner: tt.fields.CertificateSigner,
}
got, err := c.RenewCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("SoftCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SoftCAS.RenewCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func TestSoftCAS_RevokeCertificate(t *testing.T) {
type fields struct {
Issuer *x509.Certificate
Signer crypto.Signer
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
}
type args struct {
req *apiv1.RevokeCertificateRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.RevokeCertificateResponse
wantErr bool
}{
{"ok", fields{testIssuer, testSigner, nil}, 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},
{"ok no cert", fields{testIssuer, testSigner, nil}, args{&apiv1.RevokeCertificateRequest{
Reason: "test reason",
ReasonCode: 1,
}}, &apiv1.RevokeCertificateResponse{
Certificate: nil,
CertificateChain: []*x509.Certificate{testIssuer},
}, false},
{"ok empty", fields{testIssuer, testSigner, nil}, args{&apiv1.RevokeCertificateRequest{}}, &apiv1.RevokeCertificateResponse{
Certificate: nil,
CertificateChain: []*x509.Certificate{testIssuer},
}, 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 {
t.Run(tt.name, func(t *testing.T) {
c := &SoftCAS{
CertificateChain: []*x509.Certificate{tt.fields.Issuer},
Signer: tt.fields.Signer,
CertificateSigner: tt.fields.CertificateSigner,
}
got, err := c.RevokeCertificate(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("SoftCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SoftCAS.RevokeCertificate() = %v, want %v", got, tt.want)
}
})
}
}
func Test_now(t *testing.T) {
t0 := time.Now()
t1 := now()
if t1.Sub(t0) > time.Second {
t.Errorf("now() = %s, want ~%s", t1, t0)
}
}
func TestSoftCAS_CreateCertificateAuthority(t *testing.T) {
mockNow(t)
saSigner := &signatureAlgorithmSigner{
Signer: testSigner,
algorithm: x509.PureEd25519,
}
type fields struct {
Issuer *x509.Certificate
Signer crypto.Signer
KeyManager kms.KeyManager
}
type args struct {
req *apiv1.CreateCertificateAuthorityRequest
}
tests := []struct {
name string
fields fields
args args
want *apiv1.CreateCertificateAuthorityResponse
wantErr bool
}{
{"ok root", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testRootTemplate,
Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateAuthorityResponse{
Name: "Test Root CA",
Certificate: testSignedRootTemplate,
PublicKey: testSignedRootTemplate.PublicKey,
PrivateKey: testSigner,
Signer: testSigner,
}, false},
{"ok intermediate", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.IntermediateCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
Parent: &apiv1.CreateCertificateAuthorityResponse{
Certificate: testSignedRootTemplate,
Signer: testSigner,
},
}}, &apiv1.CreateCertificateAuthorityResponse{
Name: "Test Intermediate CA",
Certificate: testSignedIntermediateTemplate,
CertificateChain: []*x509.Certificate{testSignedRootTemplate},
PublicKey: testSignedIntermediateTemplate.PublicKey,
PrivateKey: testSigner,
Signer: testSigner,
}, false},
{"ok signature algorithm", fields{nil, nil, &mockKeyManager{signer: saSigner}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testRootTemplate,
Lifetime: 24 * time.Hour,
}}, &apiv1.CreateCertificateAuthorityResponse{
Name: "Test Root CA",
Certificate: testSignedRootTemplate,
PublicKey: testSignedRootTemplate.PublicKey,
PrivateKey: saSigner,
Signer: saSigner,
}, false},
{"ok createKey", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testRootTemplate,
Lifetime: 24 * time.Hour,
CreateKey: &kmsapi.CreateKeyRequest{
Name: "root_ca.crt",
SignatureAlgorithm: kmsapi.ECDSAWithSHA256,
},
}}, &apiv1.CreateCertificateAuthorityResponse{
Name: "Test Root CA",
Certificate: testSignedRootTemplate,
PublicKey: testSignedRootTemplate.PublicKey,
KeyName: "root_ca.crt",
PrivateKey: testSigner,
Signer: testSigner,
}, false},
{"fail template", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail lifetime", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testIntermediateTemplate,
}}, nil, true},
{"fail type", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail parent", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.IntermediateCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail parent.certificate", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.IntermediateCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
Parent: &apiv1.CreateCertificateAuthorityResponse{
Signer: testSigner,
},
}}, nil, true},
{"fail parent.signer", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.IntermediateCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
Parent: &apiv1.CreateCertificateAuthorityResponse{
Certificate: testSignedRootTemplate,
},
}}, nil, true},
{"fail createKey", fields{nil, nil, &mockKeyManager{errCreateKey: errTest}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail createSigner", fields{nil, nil, &mockKeyManager{errCreatesigner: errTest}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail sign root", fields{nil, nil, &mockKeyManager{signer: &badSigner{}}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.RootCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
}}, nil, true},
{"fail sign intermediate", fields{nil, nil, &mockKeyManager{}}, args{&apiv1.CreateCertificateAuthorityRequest{
Type: apiv1.IntermediateCA,
Template: testIntermediateTemplate,
Lifetime: 24 * time.Hour,
Parent: &apiv1.CreateCertificateAuthorityResponse{
Certificate: testSignedRootTemplate,
Signer: &badSigner{},
},
}}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &SoftCAS{
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 {
t.Errorf("SoftCAS.CreateCertificateAuthority() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SoftCAS.CreateCertificateAuthority() = \n%#v, want \n%#v", got, tt.want)
}
})
}
}
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
}
})
}
}