forked from TrueCloudLab/certificates
testing work in progress.
This commit is contained in:
parent
83848e9cd3
commit
54d86ca1c1
10 changed files with 482 additions and 165 deletions
|
@ -17,25 +17,25 @@ func testAuthority(t *testing.T) *Authority {
|
||||||
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
disableRenewal := true
|
disableRenewal := true
|
||||||
p := []*provisioner.Provisioner{
|
p := provisioner.List{
|
||||||
provisioner.New(&provisioner.JWK{
|
&provisioner.JWK{
|
||||||
Name: "Max",
|
Name: "Max",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: maxjwk,
|
Key: maxjwk,
|
||||||
}),
|
},
|
||||||
provisioner.New(&provisioner.JWK{
|
&provisioner.JWK{
|
||||||
Name: "step-cli",
|
Name: "step-cli",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: clijwk,
|
Key: clijwk,
|
||||||
}),
|
},
|
||||||
provisioner.New(&provisioner.JWK{
|
&provisioner.JWK{
|
||||||
Name: "dev",
|
Name: "dev",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: maxjwk,
|
Key: maxjwk,
|
||||||
Claims: &provisioner.Claims{
|
Claims: &provisioner.Claims{
|
||||||
DisableRenewal: &disableRenewal,
|
DisableRenewal: &disableRenewal,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
}
|
}
|
||||||
c := &Config{
|
c := &Config{
|
||||||
Address: "127.0.0.1:443",
|
Address: "127.0.0.1:443",
|
||||||
|
@ -114,24 +114,24 @@ func TestAuthorityNew(t *testing.T) {
|
||||||
assert.True(t, auth.initOnce)
|
assert.True(t, auth.initOnce)
|
||||||
assert.NotNil(t, auth.intermediateIdentity)
|
assert.NotNil(t, auth.intermediateIdentity)
|
||||||
for _, p := range tc.config.AuthorityConfig.Provisioners {
|
for _, p := range tc.config.AuthorityConfig.Provisioners {
|
||||||
_p, ok := auth.provisioners.Load(p.ID())
|
_p, ok := auth.provisioners.Load(p.GetID())
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equals(t, p, _p)
|
assert.Equals(t, p, _p)
|
||||||
if len(p.EncryptedKey) > 0 {
|
if kid, encryptedKey, ok := p.GetEncryptedKey(); ok {
|
||||||
key, ok := auth.provisioners.LoadEncryptedKey(p.Key.KeyID)
|
key, ok := auth.provisioners.LoadEncryptedKey(kid)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equals(t, p.EncryptedKey, key)
|
assert.Equals(t, encryptedKey, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sanity check
|
// sanity check
|
||||||
_, ok = auth.provisionerIDIndex.Load("fooo")
|
_, ok = auth.provisioners.Load("fooo")
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
|
|
||||||
assert.Equals(t, auth.audiences, []string{
|
// assert.Equals(t, auth.audiences, []string{
|
||||||
"step-certificate-authority",
|
// "step-certificate-authority",
|
||||||
"https://127.0.0.1/sign",
|
// "https://127.0.0.1/sign",
|
||||||
"https://127.0.0.1/1.0/sign",
|
// "https://127.0.0.1/1.0/sign",
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -169,7 +169,7 @@ func TestAuthorize(t *testing.T) {
|
||||||
(&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", "foo"))
|
(&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", "foo"))
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
_a.provisionerIDIndex.Store(validIssuer+":foo", "42")
|
// _a.provisioners.Store(validIssuer+":foo", "42")
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "test.smallstep.com",
|
Subject: "test.smallstep.com",
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/cli/crypto/tlsutil"
|
"github.com/smallstep/cli/crypto/tlsutil"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
"github.com/smallstep/cli/crypto/x509util"
|
||||||
stepJOSE "github.com/smallstep/cli/jose"
|
stepJOSE "github.com/smallstep/cli/jose"
|
||||||
|
@ -17,13 +18,13 @@ func TestConfigValidate(t *testing.T) {
|
||||||
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
ac := &AuthConfig{
|
ac := &AuthConfig{
|
||||||
Provisioners: []*Provisioner{
|
Provisioners: provisioner.List{
|
||||||
{
|
&provisioner.JWK{
|
||||||
Name: "Max",
|
Name: "Max",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: maxjwk,
|
Key: maxjwk,
|
||||||
},
|
},
|
||||||
{
|
&provisioner.JWK{
|
||||||
Name: "step-cli",
|
Name: "step-cli",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: clijwk,
|
Key: clijwk,
|
||||||
|
@ -229,13 +230,13 @@ func TestAuthConfigValidate(t *testing.T) {
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
clijwk, err := stepJOSE.ParseKey("testdata/secrets/step_cli_key_pub.jwk")
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
p := []*Provisioner{
|
p := provisioner.List{
|
||||||
{
|
&provisioner.JWK{
|
||||||
Name: "Max",
|
Name: "Max",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: maxjwk,
|
Key: maxjwk,
|
||||||
},
|
},
|
||||||
{
|
&provisioner.JWK{
|
||||||
Name: "step-cli",
|
Name: "step-cli",
|
||||||
Type: "JWK",
|
Type: "JWK",
|
||||||
Key: clijwk,
|
Key: clijwk,
|
||||||
|
@ -263,9 +264,9 @@ func TestAuthConfigValidate(t *testing.T) {
|
||||||
"fail-invalid-provisioners": func(t *testing.T) AuthConfigValidateTest {
|
"fail-invalid-provisioners": func(t *testing.T) AuthConfigValidateTest {
|
||||||
return AuthConfigValidateTest{
|
return AuthConfigValidateTest{
|
||||||
ac: &AuthConfig{
|
ac: &AuthConfig{
|
||||||
Provisioners: []*Provisioner{
|
Provisioners: provisioner.List{
|
||||||
{Name: "foo", Type: "bar", Key: &jose.JSONWebKey{}},
|
&provisioner.JWK{Name: "foo", Type: "bar", Key: &jose.JSONWebKey{}},
|
||||||
{Name: "foo", Key: &jose.JSONWebKey{}},
|
&provisioner.JWK{Name: "foo", Key: &jose.JSONWebKey{}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: errors.New("provisioner type cannot be empty"),
|
err: errors.New("provisioner type cannot be empty"),
|
||||||
|
@ -293,7 +294,7 @@ func TestAuthConfigValidate(t *testing.T) {
|
||||||
for name, get := range tests {
|
for name, get := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tc := get(t)
|
tc := get(t)
|
||||||
err := tc.ac.Validate()
|
err := tc.ac.Validate([]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if assert.NotNil(t, tc.err) {
|
if assert.NotNil(t, tc.err) {
|
||||||
assert.Equals(t, tc.err.Error(), err.Error())
|
assert.Equals(t, tc.err.Error(), err.Error())
|
||||||
|
|
184
authority/provisioner/collection_test.go
Normal file
184
authority/provisioner/collection_test.go
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/smallstep/assert"
|
||||||
|
|
||||||
|
"github.com/smallstep/cli/jose"
|
||||||
|
)
|
||||||
|
|
||||||
|
// func Test_newSortedProvisioners(t *testing.T) {
|
||||||
|
// provisioners := make(List, 20)
|
||||||
|
// for i := range provisioners {
|
||||||
|
// provisioners[i] = generateProvisioner(t)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ps, err := newSortedProvisioners(provisioners)
|
||||||
|
// assert.FatalError(t, err)
|
||||||
|
// prev := ""
|
||||||
|
// for i, p := range ps {
|
||||||
|
// if p.uid < prev {
|
||||||
|
// t.Errorf("%s should be less that %s", p.uid, prev)
|
||||||
|
// }
|
||||||
|
// if p.provisioner.Key.KeyID != provisioners[i].Key.KeyID {
|
||||||
|
// t.Errorf("provisioner order is not the same: %s != %s", p.provisioner.Key.KeyID, provisioners[i].Key.KeyID)
|
||||||
|
// }
|
||||||
|
// prev = p.uid
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func Test_provisionerSlice_Find(t *testing.T) {
|
||||||
|
// trim := func(s string) string {
|
||||||
|
// return strings.TrimLeft(s, "0")
|
||||||
|
// }
|
||||||
|
// provisioners := make([]*Provisioner, 20)
|
||||||
|
// for i := range provisioners {
|
||||||
|
// provisioners[i] = generateProvisioner(t)
|
||||||
|
// }
|
||||||
|
// ps, err := newSortedProvisioners(provisioners)
|
||||||
|
// assert.FatalError(t, err)
|
||||||
|
|
||||||
|
// type args struct {
|
||||||
|
// cursor string
|
||||||
|
// limit int
|
||||||
|
// }
|
||||||
|
// tests := []struct {
|
||||||
|
// name string
|
||||||
|
// p provisionerSlice
|
||||||
|
// args args
|
||||||
|
// want []*JWK
|
||||||
|
// want1 string
|
||||||
|
// }{
|
||||||
|
// {"all", ps, args{"", DefaultProvisionersMax}, provisioners[0:20], ""},
|
||||||
|
// {"0 to 19", ps, args{"", 20}, provisioners[0:20], ""},
|
||||||
|
// {"0 to 9", ps, args{"", 10}, provisioners[0:10], trim(ps[10].uid)},
|
||||||
|
// {"9 to 19", ps, args{trim(ps[10].uid), 10}, provisioners[10:20], ""},
|
||||||
|
// {"1", ps, args{trim(ps[1].uid), 1}, provisioners[1:2], trim(ps[2].uid)},
|
||||||
|
// {"1 to 5", ps, args{trim(ps[1].uid), 4}, provisioners[1:5], trim(ps[5].uid)},
|
||||||
|
// {"defaultLimit", ps, args{"", 0}, provisioners[0:20], ""},
|
||||||
|
// {"overTheLimit", ps, args{"", DefaultProvisionersMax + 1}, provisioners[0:20], ""},
|
||||||
|
// }
|
||||||
|
// for _, tt := range tests {
|
||||||
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// got, got1 := tt.p.Find(tt.args.cursor, tt.args.limit)
|
||||||
|
// if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
// t.Errorf("provisionerSlice.Find() got = %v, want %v", got, tt.want)
|
||||||
|
// }
|
||||||
|
// if got1 != tt.want1 {
|
||||||
|
// t.Errorf("provisionerSlice.Find() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func TestCollection_Load(t *testing.T) {
|
||||||
|
p, err := generateJWK()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
byID := new(sync.Map)
|
||||||
|
byID.Store(p.GetID(), p)
|
||||||
|
byID.Store("string", "a-string")
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
byID *sync.Map
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want Interface
|
||||||
|
want1 bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{byID}, args{p.GetID()}, p, true},
|
||||||
|
{"fail", fields{byID}, args{"fail"}, nil, false},
|
||||||
|
{"invalid", fields{byID}, args{"string"}, nil, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &Collection{
|
||||||
|
byID: tt.fields.byID,
|
||||||
|
}
|
||||||
|
got, got1 := c.Load(tt.args.id)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Collection.Load() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if got1 != tt.want1 {
|
||||||
|
t.Errorf("Collection.Load() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollection_LoadByToken(t *testing.T) {
|
||||||
|
p1, err := generateJWK()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
p2, err := generateJWK()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
p3, err := generateOIDC()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
byID := new(sync.Map)
|
||||||
|
byID.Store(p1.GetID(), p1)
|
||||||
|
byID.Store(p2.GetID(), p2)
|
||||||
|
byID.Store(p3.GetID(), p3)
|
||||||
|
byID.Store("string", "a-string")
|
||||||
|
|
||||||
|
jwk, err := decryptJSONWebKey(p1.EncryptedKey)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
token, err := generateSimpleToken(p1.Name, testAudiences[0], jwk)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
t1, c1, err := parseToken(token)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
jwk, err = decryptJSONWebKey(p2.EncryptedKey)
|
||||||
|
token, err = generateSimpleToken(p2.Name, testAudiences[1], jwk)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
t2, c2, err := parseToken(token)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
token, err = generateSimpleToken(p3.configuration.Issuer, p3.ClientID, &p3.keyStore.keys.Keys[0])
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
t3, c3, err := parseToken(token)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
byID *sync.Map
|
||||||
|
audiences []string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
token *jose.JSONWebToken
|
||||||
|
claims *jose.Claims
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want Interface
|
||||||
|
want1 bool
|
||||||
|
}{
|
||||||
|
{"ok1", fields{byID, testAudiences}, args{t1, c1}, p1, true},
|
||||||
|
{"ok2", fields{byID, testAudiences}, args{t2, c2}, p2, true},
|
||||||
|
{"ok3", fields{byID, testAudiences}, args{t3, c3}, p3, true},
|
||||||
|
{"fail", fields{byID, []string{"https://foo"}}, args{t1, c1}, nil, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &Collection{
|
||||||
|
byID: tt.fields.byID,
|
||||||
|
audiences: tt.fields.audiences,
|
||||||
|
}
|
||||||
|
got, got1 := c.LoadByToken(tt.args.token, tt.args.claims)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Collection.LoadByToken() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if got1 != tt.want1 {
|
||||||
|
t.Errorf("Collection.LoadByToken() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,11 +3,22 @@ package provisioner
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultDisableRenewal = false
|
||||||
|
globalProvisionerClaims = Claims{
|
||||||
|
MinTLSDur: &Duration{5 * time.Minute},
|
||||||
|
MaxTLSDur: &Duration{24 * time.Hour},
|
||||||
|
DefaultTLSDur: &Duration{24 * time.Hour},
|
||||||
|
DisableRenewal: &defaultDisableRenewal,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func TestProvisionerInit(t *testing.T) {
|
func TestProvisionerInit(t *testing.T) {
|
||||||
type ProvisionerValidateTest struct {
|
type ProvisionerValidateTest struct {
|
||||||
p *JWK
|
p *JWK
|
||||||
|
@ -39,10 +50,13 @@ func TestProvisionerInit(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
Claims: globalProvisionerClaims,
|
||||||
|
}
|
||||||
for name, get := range tests {
|
for name, get := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tc := get(t)
|
tc := get(t)
|
||||||
err := tc.p.Init(&globalProvisionerClaims)
|
err := tc.p.Init(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if assert.NotNil(t, tc.err) {
|
if assert.NotNil(t, tc.err) {
|
||||||
assert.Equals(t, tc.err.Error(), err.Error())
|
assert.Equals(t, tc.err.Error(), err.Error())
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
package authority
|
// +build ignore
|
||||||
|
|
||||||
|
package provisioner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
11
authority/provisioner/testdata/root_ca.crt
vendored
Normal file
11
authority/provisioner/testdata/root_ca.crt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBhzCCASygAwIBAgIRANJiwPnM38wWznkJGOcIyIYwCgYIKoZIzj0EAwIwITEf
|
||||||
|
MB0GA1UEAxMWU21hbGxzdGVwIFRlc3QgUm9vdCBDQTAeFw0xODA5MjcxODE4MDla
|
||||||
|
Fw0yODA5MjQxODE4MDlaMCExHzAdBgNVBAMTFlNtYWxsc3RlcCBUZXN0IFJvb3Qg
|
||||||
|
Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS15w7dx9zPjCnQ7+RlRkvUXQJN
|
||||||
|
Fjk5Hg5K9nCoiiNQQhcQMw63/pXQxHNsugiMshcN59XJC8195KJPm25nXN8co0Uw
|
||||||
|
QzAOBgNVHQ8BAf8EBAMCAaYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU
|
||||||
|
B2BAXUSPZbFjnY6VzbApV48Tn3owCgYIKoZIzj0EAwIDSQAwRgIhAJRTVmc2xW8c
|
||||||
|
ESx4oIp2d/OX9KBZzpcNi9fHnnJCS0FXAiEA7OpFb2+b8KBzg1c02x21PS7pHoET
|
||||||
|
/A8LXNH4M06A7vE=
|
||||||
|
-----END CERTIFICATE-----
|
208
authority/provisioner/utils_test.go
Normal file
208
authority/provisioner/utils_test.go
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/smallstep/cli/crypto/randutil"
|
||||||
|
"github.com/smallstep/cli/jose"
|
||||||
|
"github.com/smallstep/cli/token"
|
||||||
|
"github.com/smallstep/cli/token/provision"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testAudiences = []string{
|
||||||
|
"https://ca.smallstep.com/sign",
|
||||||
|
"https://ca.smallsteomcom/1.0/sign",
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateJSONWebKey() (*jose.JSONWebKey, error) {
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fp, err := jwk.Thumbprint(crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jwk.KeyID = string(hex.EncodeToString(fp))
|
||||||
|
return jwk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptJSONWebKey(jwk *jose.JSONWebKey) (*jose.JSONWebEncryption, error) {
|
||||||
|
b, err := json.Marshal(jwk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
salt, err := randutil.Salt(jose.PBKDF2SaltSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts := new(jose.EncrypterOptions)
|
||||||
|
opts.WithContentType(jose.ContentType("jwk+json"))
|
||||||
|
recipient := jose.Recipient{
|
||||||
|
Algorithm: jose.PBES2_HS256_A128KW,
|
||||||
|
Key: []byte("password"),
|
||||||
|
PBES2Count: jose.PBKDF2Iterations,
|
||||||
|
PBES2Salt: salt,
|
||||||
|
}
|
||||||
|
encrypter, err := jose.NewEncrypter(jose.DefaultEncAlgorithm, recipient, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return encrypter.Encrypt(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptJSONWebKey(key string) (*jose.JSONWebKey, error) {
|
||||||
|
enc, err := jose.ParseEncrypted(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := enc.Decrypt([]byte("password"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jwk := new(jose.JSONWebKey)
|
||||||
|
if err := json.Unmarshal(b, jwk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jwk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateJWK() (*JWK, error) {
|
||||||
|
name, err := randutil.Alphanumeric(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jwk, err := generateJSONWebKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jwe, err := encryptJSONWebKey(jwk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
public := jwk.Public()
|
||||||
|
encrypted, err := jwe.CompactSerialize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JWK{
|
||||||
|
Name: name,
|
||||||
|
Type: "JWK",
|
||||||
|
Key: &public,
|
||||||
|
EncryptedKey: encrypted,
|
||||||
|
audiences: testAudiences,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOIDC() (*OIDC, error) {
|
||||||
|
name, err := randutil.Alphanumeric(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clientID, err := randutil.Alphanumeric(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
issuer, err := randutil.Alphanumeric(10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jwk, err := generateJSONWebKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OIDC{
|
||||||
|
Name: name,
|
||||||
|
Type: "OIDC",
|
||||||
|
ClientID: clientID,
|
||||||
|
ConfigurationEndpoint: "https://example.com/.well-known/openid-configuration",
|
||||||
|
configuration: openIDConfiguration{
|
||||||
|
Issuer: issuer,
|
||||||
|
JWKSetURI: "https://example.com/.well-known/jwks",
|
||||||
|
},
|
||||||
|
keyStore: &keyStore{
|
||||||
|
keys: jose.JSONWebKeySet{Keys: []jose.JSONWebKey{*jwk}},
|
||||||
|
expiry: time.Now().Add(24 * time.Hour),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCollection(nJWK, nOIDC int) (*Collection, error) {
|
||||||
|
col := NewCollection(testAudiences)
|
||||||
|
for i := 0; i < nJWK; i++ {
|
||||||
|
p, err := generateJWK()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
col.Store(p)
|
||||||
|
}
|
||||||
|
for i := 0; i < nOIDC; i++ {
|
||||||
|
p, err := generateOIDC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
col.Store(p)
|
||||||
|
}
|
||||||
|
return col, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSimpleToken(iss, aud string, jwk *jose.JSONWebKey) (string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
return generateToken("the-sub", []string{"test.smallstep.com"}, jwk.KeyID, iss, aud, "testdata/root_ca.crt", now, now.Add(5*time.Minute), jwk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateToken(sub string, sans []string, kid, iss, aud, root string, notBefore, notAfter time.Time, jwk *jose.JSONWebKey) (string, error) {
|
||||||
|
// A random jwt id will be used to identify duplicated tokens
|
||||||
|
jwtID, err := randutil.Hex(64) // 256 bits
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokOptions := []token.Options{
|
||||||
|
token.WithJWTID(jwtID),
|
||||||
|
token.WithKid(kid),
|
||||||
|
token.WithIssuer(iss),
|
||||||
|
token.WithAudience(aud),
|
||||||
|
}
|
||||||
|
if len(root) > 0 {
|
||||||
|
tokOptions = append(tokOptions, token.WithRootCA(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no SANs then add the 'subject' (common-name) as the only SAN.
|
||||||
|
if len(sans) == 0 {
|
||||||
|
sans = []string{sub}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokOptions = append(tokOptions, token.WithSANS(sans))
|
||||||
|
if !notBefore.IsZero() || !notAfter.IsZero() {
|
||||||
|
if notBefore.IsZero() {
|
||||||
|
notBefore = time.Now()
|
||||||
|
}
|
||||||
|
if notAfter.IsZero() {
|
||||||
|
notAfter = notBefore.Add(token.DefaultValidity)
|
||||||
|
}
|
||||||
|
tokOptions = append(tokOptions, token.WithValidity(notBefore, notAfter))
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := provision.New(sub, tokOptions...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok.SignedString(jwk.Algorithm, jwk.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseToken(token string) (*jose.JSONWebToken, *jose.Claims, error) {
|
||||||
|
tok, err := jose.ParseSigned(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
claims := new(jose.Claims)
|
||||||
|
if err := tok.UnsafeClaimsWithoutVerification(claims); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return tok, claims, nil
|
||||||
|
}
|
|
@ -1,16 +1,12 @@
|
||||||
package authority
|
package authority
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/cli/crypto/randutil"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/cli/jose"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetEncryptedKey(t *testing.T) {
|
func TestGetEncryptedKey(t *testing.T) {
|
||||||
|
@ -27,7 +23,7 @@ func TestGetEncryptedKey(t *testing.T) {
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
return &ek{
|
return &ek{
|
||||||
a: a,
|
a: a,
|
||||||
kid: c.AuthorityConfig.Provisioners[1].Key.KeyID,
|
kid: c.AuthorityConfig.Provisioners[1].(*provisioner.JWK).Key.KeyID,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail-not-found": func(t *testing.T) *ek {
|
"fail-not-found": func(t *testing.T) *ek {
|
||||||
|
@ -42,19 +38,19 @@ func TestGetEncryptedKey(t *testing.T) {
|
||||||
http.StatusNotFound, context{}},
|
http.StatusNotFound, context{}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail-invalid-type-found": func(t *testing.T) *ek {
|
// "fail-invalid-type-found": func(t *testing.T) *ek {
|
||||||
c, err := LoadConfiguration("../ca/testdata/ca.json")
|
// c, err := LoadConfiguration("../ca/testdata/ca.json")
|
||||||
assert.FatalError(t, err)
|
// assert.FatalError(t, err)
|
||||||
a, err := New(c)
|
// a, err := New(c)
|
||||||
assert.FatalError(t, err)
|
// assert.FatalError(t, err)
|
||||||
a.encryptedKeyIndex.Store("foo", 5)
|
// a.encryptedKeyIndex.Store("foo", 5)
|
||||||
return &ek{
|
// return &ek{
|
||||||
a: a,
|
// a: a,
|
||||||
kid: "foo",
|
// kid: "foo",
|
||||||
err: &apiError{errors.Errorf("stored value is not a string"),
|
// err: &apiError{errors.Errorf("stored value is not a string"),
|
||||||
http.StatusInternalServerError, context{}},
|
// http.StatusInternalServerError, context{}},
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, genTestCase := range tests {
|
for name, genTestCase := range tests {
|
||||||
|
@ -75,9 +71,9 @@ func TestGetEncryptedKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if assert.Nil(t, tc.err) {
|
if assert.Nil(t, tc.err) {
|
||||||
val, ok := tc.a.provisionerIDIndex.Load("max:" + tc.kid)
|
val, ok := tc.a.provisioners.Load("max:" + tc.kid)
|
||||||
assert.Fatal(t, ok)
|
assert.Fatal(t, ok)
|
||||||
p, ok := val.(*Provisioner)
|
p, ok := val.(*provisioner.JWK)
|
||||||
assert.Fatal(t, ok)
|
assert.Fatal(t, ok)
|
||||||
assert.Equals(t, p.EncryptedKey, ek)
|
assert.Equals(t, p.EncryptedKey, ek)
|
||||||
}
|
}
|
||||||
|
@ -126,102 +122,3 @@ func TestGetProvisioners(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateProvisioner(t *testing.T) *Provisioner {
|
|
||||||
name, err := randutil.Alphanumeric(10)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
// Create a new JWK
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
// Encrypt JWK
|
|
||||||
salt, err := randutil.Salt(jose.PBKDF2SaltSize)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
b, err := json.Marshal(jwk)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
recipient := jose.Recipient{
|
|
||||||
Algorithm: jose.PBES2_HS256_A128KW,
|
|
||||||
Key: []byte("password"),
|
|
||||||
PBES2Count: jose.PBKDF2Iterations,
|
|
||||||
PBES2Salt: salt,
|
|
||||||
}
|
|
||||||
opts := new(jose.EncrypterOptions)
|
|
||||||
opts.WithContentType(jose.ContentType("jwk+json"))
|
|
||||||
encrypter, err := jose.NewEncrypter(jose.DefaultEncAlgorithm, recipient, opts)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
jwe, err := encrypter.Encrypt(b)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
// get public and encrypted keys
|
|
||||||
public := jwk.Public()
|
|
||||||
encrypted, err := jwe.CompactSerialize()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
return &Provisioner{
|
|
||||||
Name: name,
|
|
||||||
Type: "JWT",
|
|
||||||
Key: &public,
|
|
||||||
EncryptedKey: encrypted,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_newSortedProvisioners(t *testing.T) {
|
|
||||||
provisioners := make([]*Provisioner, 20)
|
|
||||||
for i := range provisioners {
|
|
||||||
provisioners[i] = generateProvisioner(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
ps, err := newSortedProvisioners(provisioners)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
prev := ""
|
|
||||||
for i, p := range ps {
|
|
||||||
if p.uid < prev {
|
|
||||||
t.Errorf("%s should be less that %s", p.uid, prev)
|
|
||||||
}
|
|
||||||
if p.provisioner.Key.KeyID != provisioners[i].Key.KeyID {
|
|
||||||
t.Errorf("provisioner order is not the same: %s != %s", p.provisioner.Key.KeyID, provisioners[i].Key.KeyID)
|
|
||||||
}
|
|
||||||
prev = p.uid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_provisionerSlice_Find(t *testing.T) {
|
|
||||||
trim := func(s string) string {
|
|
||||||
return strings.TrimLeft(s, "0")
|
|
||||||
}
|
|
||||||
provisioners := make([]*Provisioner, 20)
|
|
||||||
for i := range provisioners {
|
|
||||||
provisioners[i] = generateProvisioner(t)
|
|
||||||
}
|
|
||||||
ps, err := newSortedProvisioners(provisioners)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
cursor string
|
|
||||||
limit int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
p provisionerSlice
|
|
||||||
args args
|
|
||||||
want []*Provisioner
|
|
||||||
want1 string
|
|
||||||
}{
|
|
||||||
{"all", ps, args{"", DefaultProvisionersMax}, provisioners[0:20], ""},
|
|
||||||
{"0 to 19", ps, args{"", 20}, provisioners[0:20], ""},
|
|
||||||
{"0 to 9", ps, args{"", 10}, provisioners[0:10], trim(ps[10].uid)},
|
|
||||||
{"9 to 19", ps, args{trim(ps[10].uid), 10}, provisioners[10:20], ""},
|
|
||||||
{"1", ps, args{trim(ps[1].uid), 1}, provisioners[1:2], trim(ps[2].uid)},
|
|
||||||
{"1 to 5", ps, args{trim(ps[1].uid), 4}, provisioners[1:5], trim(ps[5].uid)},
|
|
||||||
{"defaultLimit", ps, args{"", 0}, provisioners[0:20], ""},
|
|
||||||
{"overTheLimit", ps, args{"", DefaultProvisionersMax + 1}, provisioners[0:20], ""},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, got1 := tt.p.Find(tt.args.cursor, tt.args.limit)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("provisionerSlice.Find() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if got1 != tt.want1 {
|
|
||||||
t.Errorf("provisionerSlice.Find() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -15,6 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/cli/crypto/keys"
|
"github.com/smallstep/cli/crypto/keys"
|
||||||
"github.com/smallstep/cli/crypto/tlsutil"
|
"github.com/smallstep/cli/crypto/tlsutil"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
"github.com/smallstep/cli/crypto/x509util"
|
||||||
|
@ -52,24 +52,24 @@ func TestSign(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nb := time.Now()
|
nb := time.Now()
|
||||||
signOpts := SignOptions{
|
signOpts := provisioner.Options{
|
||||||
NotBefore: nb,
|
NotBefore: nb,
|
||||||
NotAfter: nb.Add(time.Minute * 5),
|
NotAfter: nb.Add(time.Minute * 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
p := a.config.AuthorityConfig.Provisioners[1]
|
p := a.config.AuthorityConfig.Provisioners[1].(*provisioner.JWK)
|
||||||
extraOpts := []interface{}{
|
extraOpts := []provisioner.SignOption{
|
||||||
&commonNameClaim{"smallstep test"},
|
// &commonNameClaim{"smallstep test"},
|
||||||
&dnsNamesClaim{[]string{"test.smallstep.com"}},
|
// &dnsNamesClaim{[]string{"test.smallstep.com"}},
|
||||||
&ipAddressesClaim{[]net.IP{}},
|
// &ipAddressesClaim{[]net.IP{}},
|
||||||
p,
|
// p,
|
||||||
}
|
}
|
||||||
|
|
||||||
type signTest struct {
|
type signTest struct {
|
||||||
auth *Authority
|
auth *Authority
|
||||||
csr *x509.CertificateRequest
|
csr *x509.CertificateRequest
|
||||||
signOpts SignOptions
|
signOpts provisioner.Options
|
||||||
extraOpts []interface{}
|
extraOpts []provisioner.SignOption
|
||||||
err *apiError
|
err *apiError
|
||||||
}
|
}
|
||||||
tests := map[string]func(*testing.T) *signTest{
|
tests := map[string]func(*testing.T) *signTest{
|
||||||
|
@ -123,7 +123,7 @@ func TestSign(t *testing.T) {
|
||||||
return &signTest{
|
return &signTest{
|
||||||
auth: _a,
|
auth: _a,
|
||||||
csr: csr,
|
csr: csr,
|
||||||
extraOpts: []interface{}{p},
|
extraOpts: []provisioner.SignOption{p},
|
||||||
signOpts: signOpts,
|
signOpts: signOpts,
|
||||||
err: &apiError{errors.New("sign: error creating new leaf certificate"),
|
err: &apiError{errors.New("sign: error creating new leaf certificate"),
|
||||||
http.StatusInternalServerError,
|
http.StatusInternalServerError,
|
||||||
|
@ -133,7 +133,7 @@ func TestSign(t *testing.T) {
|
||||||
},
|
},
|
||||||
"fail provisioner duration claim": func(t *testing.T) *signTest {
|
"fail provisioner duration claim": func(t *testing.T) *signTest {
|
||||||
csr := getCSR(t, priv)
|
csr := getCSR(t, priv)
|
||||||
_signOpts := SignOptions{
|
_signOpts := provisioner.Options{
|
||||||
NotBefore: nb,
|
NotBefore: nb,
|
||||||
NotAfter: nb.Add(time.Hour * 25),
|
NotAfter: nb.Add(time.Hour * 25),
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ func TestRenew(t *testing.T) {
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
nb1 := now.Add(-time.Minute * 7)
|
nb1 := now.Add(-time.Minute * 7)
|
||||||
na1 := now
|
na1 := now
|
||||||
so := &SignOptions{
|
so := &provisioner.Options{
|
||||||
NotBefore: nb1,
|
NotBefore: nb1,
|
||||||
NotAfter: na1,
|
NotAfter: na1,
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ func TestRenew(t *testing.T) {
|
||||||
x509util.WithNotBeforeAfterDuration(so.NotBefore, so.NotAfter, 0),
|
x509util.WithNotBeforeAfterDuration(so.NotBefore, so.NotAfter, 0),
|
||||||
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
||||||
x509util.WithPublicKey(pub), x509util.WithHosts("test.smallstep.com,test"),
|
x509util.WithPublicKey(pub), x509util.WithHosts("test.smallstep.com,test"),
|
||||||
withProvisionerOID("Max", a.config.AuthorityConfig.Provisioners[0].Key.KeyID))
|
withProvisionerOID("Max", a.config.AuthorityConfig.Provisioners[0].(*provisioner.JWK).Key.KeyID))
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
crtBytes, err := leaf.CreateCertificate()
|
crtBytes, err := leaf.CreateCertificate()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
@ -284,7 +284,7 @@ func TestRenew(t *testing.T) {
|
||||||
x509util.WithNotBeforeAfterDuration(so.NotBefore, so.NotAfter, 0),
|
x509util.WithNotBeforeAfterDuration(so.NotBefore, so.NotAfter, 0),
|
||||||
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
||||||
x509util.WithPublicKey(pub), x509util.WithHosts("test.smallstep.com,test"),
|
x509util.WithPublicKey(pub), x509util.WithHosts("test.smallstep.com,test"),
|
||||||
withProvisionerOID("dev", a.config.AuthorityConfig.Provisioners[2].Key.KeyID),
|
withProvisionerOID("dev", a.config.AuthorityConfig.Provisioners[2].(*provisioner.JWK).Key.KeyID),
|
||||||
)
|
)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
crtBytesNoRenew, err := leafNoRenew.CreateCertificate()
|
crtBytesNoRenew, err := leafNoRenew.CreateCertificate()
|
||||||
|
|
Loading…
Reference in a new issue