Add JWK as an issuer for stepcas.
This commit is contained in:
parent
ce3e6bfdf6
commit
80542d6d9a
6 changed files with 517 additions and 78 deletions
|
@ -1,12 +1,36 @@
|
|||
package stepcas
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
)
|
||||
|
||||
type stepIssuer interface {
|
||||
SignToken(subject string, sans []string) (string, error)
|
||||
RevokeToken(subject string) (string, error)
|
||||
Lifetime(d time.Duration) time.Duration
|
||||
}
|
||||
|
||||
// newStepIssuer returns the configured step issuer.
|
||||
func newStepIssuer(caURL *url.URL, iss *apiv1.CertificateIssuer) (stepIssuer, error) {
|
||||
if err := validateCertificateIssuer(iss); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch strings.ToLower(iss.Type) {
|
||||
case "x5c":
|
||||
return newX5CIssuer(caURL, iss)
|
||||
case "jwk":
|
||||
return newJWKIssuer(caURL, iss)
|
||||
default:
|
||||
return nil, errors.Errorf("stepCAS `certificateIssuer.type` %s is not supported", iss.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// validateCertificateIssuer validates the configuration of the certificate
|
||||
// issuer.
|
||||
func validateCertificateIssuer(iss *apiv1.CertificateIssuer) error {
|
||||
|
@ -20,6 +44,8 @@ func validateCertificateIssuer(iss *apiv1.CertificateIssuer) error {
|
|||
switch strings.ToLower(iss.Type) {
|
||||
case "x5c":
|
||||
return validateX5CIssuer(iss)
|
||||
case "jwk":
|
||||
return validateJWKIssuer(iss)
|
||||
default:
|
||||
return errors.Errorf("stepCAS `certificateIssuer.type` %s is not supported", iss.Type)
|
||||
}
|
||||
|
@ -38,3 +64,15 @@ func validateX5CIssuer(iss *apiv1.CertificateIssuer) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validateJWKIssuer validates the configuration of jwk issuer.
|
||||
func validateJWKIssuer(iss *apiv1.CertificateIssuer) error {
|
||||
switch {
|
||||
case iss.Key == "":
|
||||
return errors.New("stepCAS `certificateIssuer.key` cannot be empty")
|
||||
case iss.Provisioner == "":
|
||||
return errors.New("stepCAS `certificateIssuer.provisioner` cannot be empty")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
80
cas/stepcas/issuer_test.go
Normal file
80
cas/stepcas/issuer_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package stepcas
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
)
|
||||
|
||||
type mockErrIssuer struct{}
|
||||
|
||||
func (m mockErrIssuer) SignToken(subject string, sans []string) (string, error) {
|
||||
return "", apiv1.ErrNotImplemented{}
|
||||
}
|
||||
|
||||
func (m mockErrIssuer) RevokeToken(subject string) (string, error) {
|
||||
return "", apiv1.ErrNotImplemented{}
|
||||
}
|
||||
|
||||
func (m mockErrIssuer) Lifetime(d time.Duration) time.Duration {
|
||||
return d
|
||||
}
|
||||
|
||||
func Test_newStepIssuer(t *testing.T) {
|
||||
caURL, err := url.Parse("https://ca.smallstep.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
caURL *url.URL
|
||||
iss *apiv1.CertificateIssuer
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want stepIssuer
|
||||
wantErr bool
|
||||
}{
|
||||
{"x5c", args{caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
}}, &x5cIssuer{
|
||||
caURL: caURL,
|
||||
certFile: testX5CPath,
|
||||
keyFile: testX5CKeyPath,
|
||||
issuer: "X5C",
|
||||
}, false},
|
||||
{"jwk", args{caURL, &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "ra@doe.org",
|
||||
Key: testX5CKeyPath,
|
||||
}}, &jwkIssuer{
|
||||
caURL: caURL,
|
||||
keyFile: testX5CKeyPath,
|
||||
issuer: "ra@doe.org",
|
||||
}, false},
|
||||
{"fail", args{caURL, &apiv1.CertificateIssuer{
|
||||
Type: "unknown",
|
||||
Provisioner: "ra@doe.org",
|
||||
Key: testX5CKeyPath,
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := newStepIssuer(tt.args.caURL, tt.args.iss)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("newStepIssuer() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("newStepIssuer() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
96
cas/stepcas/jwk_issuer.go
Normal file
96
cas/stepcas/jwk_issuer.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package stepcas
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
"go.step.sm/crypto/jose"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
"go.step.sm/crypto/randutil"
|
||||
)
|
||||
|
||||
type jwkIssuer struct {
|
||||
caURL *url.URL
|
||||
keyFile string
|
||||
issuer string
|
||||
}
|
||||
|
||||
func newJWKIssuer(caURL *url.URL, cfg *apiv1.CertificateIssuer) (*jwkIssuer, error) {
|
||||
_, err := newJWKSigner(cfg.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &jwkIssuer{
|
||||
caURL: caURL,
|
||||
keyFile: cfg.Key,
|
||||
issuer: cfg.Provisioner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *jwkIssuer) SignToken(subject string, sans []string) (string, error) {
|
||||
aud := i.caURL.ResolveReference(&url.URL{
|
||||
Path: "/1.0/sign",
|
||||
}).String()
|
||||
return i.createToken(aud, subject, sans)
|
||||
}
|
||||
|
||||
func (i *jwkIssuer) RevokeToken(subject string) (string, error) {
|
||||
aud := i.caURL.ResolveReference(&url.URL{
|
||||
Path: "/1.0/revoke",
|
||||
}).String()
|
||||
return i.createToken(aud, subject, nil)
|
||||
}
|
||||
|
||||
func (i *jwkIssuer) Lifetime(d time.Duration) time.Duration {
|
||||
return d
|
||||
}
|
||||
|
||||
func (i *jwkIssuer) createToken(aud, sub string, sans []string) (string, error) {
|
||||
signer, err := newJWKSigner(i.keyFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
id, err := randutil.Hex(64) // 256 bits
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
claims := defaultClaims(i.issuer, sub, aud, id)
|
||||
builder := jose.Signed(signer).Claims(claims)
|
||||
if len(sans) > 0 {
|
||||
builder = builder.Claims(map[string]interface{}{
|
||||
"sans": sans,
|
||||
})
|
||||
}
|
||||
|
||||
tok, err := builder.CompactSerialize()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error signing token")
|
||||
}
|
||||
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func newJWKSigner(keyFile string) (jose.Signer, error) {
|
||||
key, err := pemutil.Read(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil, errors.New("key is not a crypto.Signer")
|
||||
}
|
||||
kid, err := jose.Thumbprint(&jose.JSONWebKey{Key: signer.Public()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
so := new(jose.SignerOptions)
|
||||
so.WithType("JWT")
|
||||
so.WithHeader("kid", kid)
|
||||
return newJoseSigner(signer, so)
|
||||
}
|
172
cas/stepcas/jwk_issuer_test.go
Normal file
172
cas/stepcas/jwk_issuer_test.go
Normal file
|
@ -0,0 +1,172 @@
|
|||
package stepcas
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.step.sm/crypto/jose"
|
||||
)
|
||||
|
||||
func Test_jwkIssuer_SignToken(t *testing.T) {
|
||||
caURL, err := url.Parse("https://ca.smallstep.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
caURL *url.URL
|
||||
keyFile string
|
||||
issuer string
|
||||
}
|
||||
type args struct {
|
||||
subject string
|
||||
sans []string
|
||||
}
|
||||
type claims struct {
|
||||
Aud []string `json:"aud"`
|
||||
Sub string `json:"sub"`
|
||||
Sans []string `json:"sans"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{caURL, testX5CKeyPath, "ra@doe.org"}, args{"doe", []string{"doe.org"}}, false},
|
||||
{"fail key", fields{caURL, "", "ra@doe.org"}, args{"doe", []string{"doe.org"}}, true},
|
||||
{"fail no signer", fields{caURL, testIssPath, "ra@doe.org"}, args{"doe", []string{"doe.org"}}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := &jwkIssuer{
|
||||
caURL: tt.fields.caURL,
|
||||
keyFile: tt.fields.keyFile,
|
||||
issuer: tt.fields.issuer,
|
||||
}
|
||||
got, err := i.SignToken(tt.args.subject, tt.args.sans)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("jwkIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr {
|
||||
jwt, err := jose.ParseSigned(got)
|
||||
if err != nil {
|
||||
t.Errorf("jose.ParseSigned() error = %v", err)
|
||||
}
|
||||
var c claims
|
||||
want := claims{
|
||||
Aud: []string{tt.fields.caURL.String() + "/1.0/sign"},
|
||||
Sub: tt.args.subject,
|
||||
Sans: tt.args.sans,
|
||||
}
|
||||
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
||||
t.Errorf("jwt.Claims() error = %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c, want) {
|
||||
t.Errorf("jwt.Claims() claims = %#v, want %#v", c, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_jwkIssuer_RevokeToken(t *testing.T) {
|
||||
caURL, err := url.Parse("https://ca.smallstep.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
caURL *url.URL
|
||||
keyFile string
|
||||
issuer string
|
||||
}
|
||||
type args struct {
|
||||
subject string
|
||||
}
|
||||
type claims struct {
|
||||
Aud []string `json:"aud"`
|
||||
Sub string `json:"sub"`
|
||||
Sans []string `json:"sans"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{caURL, testX5CKeyPath, "ra@smallstep.com"}, args{"doe"}, false},
|
||||
{"fail key", fields{caURL, "", "ra@smallstep.com"}, args{"doe"}, true},
|
||||
{"fail no signer", fields{caURL, testIssPath, "ra@smallstep.com"}, args{"doe"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := &jwkIssuer{
|
||||
caURL: tt.fields.caURL,
|
||||
keyFile: tt.fields.keyFile,
|
||||
issuer: tt.fields.issuer,
|
||||
}
|
||||
got, err := i.RevokeToken(tt.args.subject)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("jwkIssuer.RevokeToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr {
|
||||
jwt, err := jose.ParseSigned(got)
|
||||
if err != nil {
|
||||
t.Errorf("jose.ParseSigned() error = %v", err)
|
||||
}
|
||||
var c claims
|
||||
want := claims{
|
||||
Aud: []string{tt.fields.caURL.String() + "/1.0/revoke"},
|
||||
Sub: tt.args.subject,
|
||||
}
|
||||
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
||||
t.Errorf("jwt.Claims() error = %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c, want) {
|
||||
t.Errorf("jwt.Claims() claims = %#v, want %#v", c, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_jwkIssuer_Lifetime(t *testing.T) {
|
||||
caURL, err := url.Parse("https://ca.smallstep.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
caURL *url.URL
|
||||
keyFile string
|
||||
issuer string
|
||||
}
|
||||
type args struct {
|
||||
d time.Duration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want time.Duration
|
||||
}{
|
||||
{"ok", fields{caURL, testX5CKeyPath, "ra@smallstep.com"}, args{time.Second}, time.Second},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := &jwkIssuer{
|
||||
caURL: tt.fields.caURL,
|
||||
keyFile: tt.fields.keyFile,
|
||||
issuer: tt.fields.issuer,
|
||||
}
|
||||
if got := i.Lifetime(tt.args.d); got != tt.want {
|
||||
t.Errorf("jwkIssuer.Lifetime() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ func init() {
|
|||
// StepCAS implements the cas.CertificateAuthorityService interface using
|
||||
// another step-ca instance.
|
||||
type StepCAS struct {
|
||||
x5c *x5cIssuer
|
||||
iss stepIssuer
|
||||
client *ca.Client
|
||||
fingerprint string
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) {
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "stepCAS `certificateAuthority` is not valid")
|
||||
}
|
||||
if err := validateCertificateIssuer(opts.CertificateIssuer); err != nil {
|
||||
|
||||
// Create configured issuer
|
||||
iss, err := newStepIssuer(caURL, opts.CertificateIssuer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -50,14 +53,8 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// X5C is the only one supported at the moment.
|
||||
x5c, err := newX5CIssuer(caURL, opts.CertificateIssuer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StepCAS{
|
||||
x5c: x5c,
|
||||
iss: iss,
|
||||
client: client,
|
||||
fingerprint: opts.CertificateAuthorityFingerprint,
|
||||
}, nil
|
||||
|
@ -101,7 +98,7 @@ func (s *StepCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1
|
|||
serialNumber = req.Certificate.SerialNumber.String()
|
||||
}
|
||||
|
||||
token, err := s.revokeToken(serialNumber)
|
||||
token, err := s.iss.RevokeToken(serialNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -151,7 +148,7 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D
|
|||
commonName = sans[0]
|
||||
}
|
||||
|
||||
token, err := s.signToken(commonName, sans)
|
||||
token, err := s.iss.SignToken(commonName, sans)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -174,27 +171,8 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D
|
|||
return cert, chain, nil
|
||||
}
|
||||
|
||||
func (s *StepCAS) signToken(subject string, sans []string) (string, error) {
|
||||
if s.x5c != nil {
|
||||
return s.x5c.SignToken(subject, sans)
|
||||
}
|
||||
|
||||
return "", errors.New("stepCAS does not have any provisioner configured")
|
||||
}
|
||||
|
||||
func (s *StepCAS) revokeToken(subject string) (string, error) {
|
||||
if s.x5c != nil {
|
||||
return s.x5c.RevokeToken(subject)
|
||||
}
|
||||
|
||||
return "", errors.New("stepCAS does not have any provisioner configured")
|
||||
}
|
||||
|
||||
func (s *StepCAS) lifetime(d time.Duration) api.TimeDuration {
|
||||
if s.x5c != nil {
|
||||
d = s.x5c.Lifetime(d)
|
||||
}
|
||||
var td api.TimeDuration
|
||||
td.SetDuration(d)
|
||||
td.SetDuration(s.iss.Lifetime(d))
|
||||
return td
|
||||
}
|
||||
|
|
|
@ -167,6 +167,33 @@ func testCAHelper(t *testing.T) (*url.URL, *ca.Client) {
|
|||
return u, client
|
||||
}
|
||||
|
||||
func testX5CIssuer(t *testing.T, caURL *url.URL) *x5cIssuer {
|
||||
t.Helper()
|
||||
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return x5c
|
||||
}
|
||||
|
||||
func testJWKIssuer(t *testing.T, caURL *url.URL) *jwkIssuer {
|
||||
t.Helper()
|
||||
x5c, err := newJWKIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "ra@doe.org",
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return x5c
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testRootCrt, testRootKey = mustSignCertificate("Test Root Certificate", nil, x509util.DefaultRootTemplate, nil, nil)
|
||||
testIssCrt, testIssKey = mustSignCertificate("Test Intermediate Certificate", nil, x509util.DefaultIntermediateTemplate, testRootCrt, testRootKey)
|
||||
|
@ -258,7 +285,7 @@ func TestNew(t *testing.T) {
|
|||
Key: testX5CKeyPath,
|
||||
},
|
||||
}}, &StepCAS{
|
||||
x5c: &x5cIssuer{
|
||||
iss: &x5cIssuer{
|
||||
caURL: caURL,
|
||||
certFile: testX5CPath,
|
||||
keyFile: testX5CKeyPath,
|
||||
|
@ -267,6 +294,23 @@ func TestNew(t *testing.T) {
|
|||
client: client,
|
||||
fingerprint: testRootFingerprint,
|
||||
}, false},
|
||||
{"ok jwk", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
CertificateIssuer: &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "ra@doe.org",
|
||||
Key: testX5CKeyPath,
|
||||
},
|
||||
}}, &StepCAS{
|
||||
iss: &jwkIssuer{
|
||||
caURL: caURL,
|
||||
keyFile: testX5CKeyPath,
|
||||
issuer: "ra@doe.org",
|
||||
},
|
||||
client: client,
|
||||
fingerprint: testRootFingerprint,
|
||||
}, false},
|
||||
{"fail authority", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: "",
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
|
@ -307,6 +351,15 @@ func TestNew(t *testing.T) {
|
|||
Key: testX5CKeyPath,
|
||||
},
|
||||
}}, nil, true},
|
||||
{"fail provisioner jwk", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
CertificateIssuer: &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "",
|
||||
Key: testX5CKeyPath,
|
||||
},
|
||||
}}, nil, true},
|
||||
{"fail certificate", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
|
@ -327,6 +380,15 @@ func TestNew(t *testing.T) {
|
|||
Key: "",
|
||||
},
|
||||
}}, nil, true},
|
||||
{"fail key jwk", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
CertificateIssuer: &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "ra@smallstep.com",
|
||||
Key: "",
|
||||
},
|
||||
}}, nil, true},
|
||||
{"bad authority", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: "https://foobar",
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
|
@ -367,6 +429,15 @@ func TestNew(t *testing.T) {
|
|||
Key: testX5CKeyPath,
|
||||
},
|
||||
}}, nil, true},
|
||||
{"fail new jwk issuer", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
CertificateIssuer: &apiv1.CertificateIssuer{
|
||||
Type: "jwk",
|
||||
Provisioner: "ra@doe.org",
|
||||
Key: testX5CKeyPath + ".missing",
|
||||
},
|
||||
}}, nil, true},
|
||||
{"bad issuer", args{context.TODO(), apiv1.Options{
|
||||
CertificateAuthority: caURL.String(),
|
||||
CertificateAuthorityFingerprint: testRootFingerprint,
|
||||
|
@ -402,18 +473,11 @@ func TestNew(t *testing.T) {
|
|||
|
||||
func TestStepCAS_CreateCertificate(t *testing.T) {
|
||||
caURL, client := testCAHelper(t)
|
||||
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x5c := testX5CIssuer(t, caURL)
|
||||
jwk := testJWKIssuer(t, caURL)
|
||||
|
||||
type fields struct {
|
||||
x5c *x5cIssuer
|
||||
iss stepIssuer
|
||||
client *ca.Client
|
||||
fingerprint string
|
||||
}
|
||||
|
@ -434,6 +498,13 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
|||
Certificate: testCrt,
|
||||
CertificateChain: []*x509.Certificate{testIssCrt},
|
||||
}, false},
|
||||
{"ok jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||
CSR: testCR,
|
||||
Lifetime: time.Hour,
|
||||
}}, &apiv1.CreateCertificateResponse{
|
||||
Certificate: testCrt,
|
||||
CertificateChain: []*x509.Certificate{testIssCrt},
|
||||
}, false},
|
||||
{"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||
CSR: nil,
|
||||
Lifetime: time.Hour,
|
||||
|
@ -442,7 +513,7 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
|||
CSR: testCR,
|
||||
Lifetime: 0,
|
||||
}}, nil, true},
|
||||
{"fail sign token", fields{nil, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||
{"fail sign token", fields{mockErrIssuer{}, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||
CSR: testCR,
|
||||
Lifetime: time.Hour,
|
||||
}}, nil, true},
|
||||
|
@ -454,7 +525,7 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &StepCAS{
|
||||
x5c: tt.fields.x5c,
|
||||
iss: tt.fields.iss,
|
||||
client: tt.fields.client,
|
||||
fingerprint: tt.fields.fingerprint,
|
||||
}
|
||||
|
@ -472,18 +543,11 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
|||
|
||||
func TestStepCAS_RenewCertificate(t *testing.T) {
|
||||
caURL, client := testCAHelper(t)
|
||||
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x5c := testX5CIssuer(t, caURL)
|
||||
jwk := testJWKIssuer(t, caURL)
|
||||
|
||||
type fields struct {
|
||||
x5c *x5cIssuer
|
||||
iss stepIssuer
|
||||
client *ca.Client
|
||||
fingerprint string
|
||||
}
|
||||
|
@ -501,11 +565,15 @@ func TestStepCAS_RenewCertificate(t *testing.T) {
|
|||
CSR: testCR,
|
||||
Lifetime: time.Hour,
|
||||
}}, nil, true},
|
||||
{"not implemented jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.RenewCertificateRequest{
|
||||
CSR: testCR,
|
||||
Lifetime: time.Hour,
|
||||
}}, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &StepCAS{
|
||||
x5c: tt.fields.x5c,
|
||||
iss: tt.fields.iss,
|
||||
client: tt.fields.client,
|
||||
fingerprint: tt.fields.fingerprint,
|
||||
}
|
||||
|
@ -523,18 +591,11 @@ func TestStepCAS_RenewCertificate(t *testing.T) {
|
|||
|
||||
func TestStepCAS_RevokeCertificate(t *testing.T) {
|
||||
caURL, client := testCAHelper(t)
|
||||
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x5c := testX5CIssuer(t, caURL)
|
||||
jwk := testJWKIssuer(t, caURL)
|
||||
|
||||
type fields struct {
|
||||
x5c *x5cIssuer
|
||||
iss stepIssuer
|
||||
client *ca.Client
|
||||
fingerprint string
|
||||
}
|
||||
|
@ -564,11 +625,27 @@ func TestStepCAS_RevokeCertificate(t *testing.T) {
|
|||
}}, &apiv1.RevokeCertificateResponse{
|
||||
Certificate: testCrt,
|
||||
}, false},
|
||||
{"ok serial number jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
SerialNumber: "ok",
|
||||
Certificate: nil,
|
||||
}}, &apiv1.RevokeCertificateResponse{}, false},
|
||||
{"ok certificate jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
SerialNumber: "",
|
||||
Certificate: testCrt,
|
||||
}}, &apiv1.RevokeCertificateResponse{
|
||||
Certificate: testCrt,
|
||||
}, false},
|
||||
{"ok both jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
SerialNumber: "ok",
|
||||
Certificate: testCrt,
|
||||
}}, &apiv1.RevokeCertificateResponse{
|
||||
Certificate: testCrt,
|
||||
}, false},
|
||||
{"fail request", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
SerialNumber: "",
|
||||
Certificate: nil,
|
||||
}}, nil, true},
|
||||
{"fail revoke token", fields{nil, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
{"fail revoke token", fields{mockErrIssuer{}, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
SerialNumber: "ok",
|
||||
}}, nil, true},
|
||||
{"fail client revoke", fields{x5c, client, testRootFingerprint}, args{&apiv1.RevokeCertificateRequest{
|
||||
|
@ -578,7 +655,7 @@ func TestStepCAS_RevokeCertificate(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &StepCAS{
|
||||
x5c: tt.fields.x5c,
|
||||
iss: tt.fields.iss,
|
||||
client: tt.fields.client,
|
||||
fingerprint: tt.fields.fingerprint,
|
||||
}
|
||||
|
@ -596,18 +673,11 @@ func TestStepCAS_RevokeCertificate(t *testing.T) {
|
|||
|
||||
func TestStepCAS_GetCertificateAuthority(t *testing.T) {
|
||||
caURL, client := testCAHelper(t)
|
||||
x5c, err := newX5CIssuer(caURL, &apiv1.CertificateIssuer{
|
||||
Type: "x5c",
|
||||
Provisioner: "X5C",
|
||||
Certificate: testX5CPath,
|
||||
Key: testX5CKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x5c := testX5CIssuer(t, caURL)
|
||||
jwk := testJWKIssuer(t, caURL)
|
||||
|
||||
type fields struct {
|
||||
x5c *x5cIssuer
|
||||
iss stepIssuer
|
||||
client *ca.Client
|
||||
fingerprint string
|
||||
}
|
||||
|
@ -626,6 +696,11 @@ func TestStepCAS_GetCertificateAuthority(t *testing.T) {
|
|||
}}, &apiv1.GetCertificateAuthorityResponse{
|
||||
RootCertificate: testRootCrt,
|
||||
}, false},
|
||||
{"ok jwk", fields{jwk, client, testRootFingerprint}, args{&apiv1.GetCertificateAuthorityRequest{
|
||||
Name: caURL.String(),
|
||||
}}, &apiv1.GetCertificateAuthorityResponse{
|
||||
RootCertificate: testRootCrt,
|
||||
}, false},
|
||||
{"fail fingerprint", fields{x5c, client, "fail"}, args{&apiv1.GetCertificateAuthorityRequest{
|
||||
Name: caURL.String(),
|
||||
}}, nil, true},
|
||||
|
@ -633,7 +708,7 @@ func TestStepCAS_GetCertificateAuthority(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &StepCAS{
|
||||
x5c: tt.fields.x5c,
|
||||
iss: tt.fields.iss,
|
||||
client: tt.fields.client,
|
||||
fingerprint: tt.fields.fingerprint,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue