Enforce >= 2048 bit rsa keys at the provisioner layer

* Fixes #94
* In the future this should be configurable by provisioner
This commit is contained in:
max furman 2019-08-26 17:52:49 -07:00
parent 0939f0d053
commit 2b41faa9cf
22 changed files with 250 additions and 39 deletions

View file

@ -24,6 +24,8 @@ import (
"time"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"github.com/smallstep/assert"
"github.com/smallstep/certificates/authority"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/logging"
@ -150,7 +152,7 @@ func parseCertificate(data string) *x509.Certificate {
}
func parseCertificateRequest(data string) *x509.CertificateRequest {
block, _ := pem.Decode([]byte(csrPEM))
block, _ := pem.Decode([]byte(data))
if block == nil {
panic("failed to parse certificate request PEM")
}
@ -387,11 +389,11 @@ func TestSignRequest_Validate(t *testing.T) {
tests := []struct {
name string
fields fields
wantErr bool
err error
}{
{"missing csr", fields{CertificateRequest{}, "foobarzar", time.Time{}, time.Time{}}, true},
{"invalid csr", fields{CertificateRequest{bad}, "foobarzar", time.Time{}, time.Time{}}, true},
{"missing ott", fields{CertificateRequest{csr}, "", time.Time{}, time.Time{}}, true},
{"missing csr", fields{CertificateRequest{}, "foobarzar", time.Time{}, time.Time{}}, errors.New("missing csr")},
{"invalid csr", fields{CertificateRequest{bad}, "foobarzar", time.Time{}, time.Time{}}, errors.New("invalid csr")},
{"missing ott", fields{CertificateRequest{csr}, "", time.Time{}, time.Time{}}, errors.New("missing ott")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -401,8 +403,12 @@ func TestSignRequest_Validate(t *testing.T) {
NotAfter: NewTimeDuration(tt.fields.NotAfter),
NotBefore: NewTimeDuration(tt.fields.NotBefore),
}
if err := s.Validate(); (err != nil) != tt.wantErr {
t.Errorf("SignRequest.Validate() error = %v, wantErr %v", err, tt.wantErr)
if err := s.Validate(); err != nil {
if assert.NotNil(t, tt.err) {
assert.HasPrefix(t, err.Error(), tt.err.Error())
}
} else {
assert.Nil(t, tt.err)
}
})
}

View file

@ -446,7 +446,7 @@ func TestAuthority_AuthorizeSign(t *testing.T) {
}
} else {
if assert.Nil(t, tc.err) {
assert.Len(t, 7, got)
assert.Len(t, 8, got)
}
}
})
@ -538,7 +538,7 @@ func TestAuthority_Authorize(t *testing.T) {
}
} else {
if assert.Nil(t, tc.err) {
assert.Len(t, 7, got)
assert.Len(t, 8, got)
}
}
})

View file

@ -287,6 +287,7 @@ func (p *AWS) AuthorizeSign(token string) ([]SignOption, error) {
}
return append(so,
defaultPublicKeyValidator{},
commonNameValidator(payload.Claims.Subject),
profileDefaultDuration(p.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeAWS, p.Name, doc.AccountID, "InstanceID", doc.InstanceID),

View file

@ -326,11 +326,11 @@ func TestAWS_AuthorizeSign(t *testing.T) {
wantLen int
wantErr bool
}{
{"ok", p1, args{t1}, 4, false},
{"ok", p2, args{t2}, 6, false},
{"ok", p2, args{t2Hostname}, 6, false},
{"ok", p2, args{t2PrivateIP}, 6, false},
{"ok", p1, args{t4}, 4, false},
{"ok", p1, args{t1}, 5, false},
{"ok", p2, args{t2}, 7, false},
{"ok", p2, args{t2Hostname}, 7, false},
{"ok", p2, args{t2PrivateIP}, 7, false},
{"ok", p1, args{t4}, 5, false},
{"fail account", p3, args{t3}, 0, true},
{"fail token", p1, args{"token"}, 0, true},
{"fail subject", p1, args{failSubject}, 0, true},

View file

@ -275,6 +275,7 @@ func (p *Azure) AuthorizeSign(token string) ([]SignOption, error) {
}
return append(so,
defaultPublicKeyValidator{},
profileDefaultDuration(p.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeAzure, p.Name, p.TenantID),
newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()),

View file

@ -281,9 +281,9 @@ func TestAzure_AuthorizeSign(t *testing.T) {
wantLen int
wantErr bool
}{
{"ok", p1, args{t1}, 3, false},
{"ok", p2, args{t2}, 5, false},
{"ok", p1, args{t11}, 3, false},
{"ok", p1, args{t1}, 4, false},
{"ok", p2, args{t2}, 6, false},
{"ok", p1, args{t11}, 4, false},
{"fail tenant", p3, args{t3}, 0, true},
{"fail resource group", p4, args{t4}, 0, true},
{"fail token", p1, args{"token"}, 0, true},

View file

@ -228,6 +228,7 @@ func (p *GCP) AuthorizeSign(token string) ([]SignOption, error) {
}
return append(so,
defaultPublicKeyValidator{},
profileDefaultDuration(p.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeGCP, p.Name, claims.Subject, "InstanceID", ce.InstanceID, "InstanceName", ce.InstanceName),
newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()),

View file

@ -311,9 +311,9 @@ func TestGCP_AuthorizeSign(t *testing.T) {
wantLen int
wantErr bool
}{
{"ok", p1, args{t1}, 3, false},
{"ok", p2, args{t2}, 5, false},
{"ok", p3, args{t3}, 3, false},
{"ok", p1, args{t1}, 4, false},
{"ok", p2, args{t2}, 6, false},
{"ok", p3, args{t3}, 4, false},
{"fail token", p1, args{"token"}, 0, true},
{"fail key", p1, args{failKey}, 0, true},
{"fail iss", p1, args{failIss}, 0, true},

View file

@ -143,6 +143,7 @@ func (p *JWK) AuthorizeSign(token string) ([]SignOption, error) {
dnsNames, ips, emails := x509util.SplitSANs(claims.SANs)
return []SignOption{
defaultPublicKeyValidator{},
commonNameValidator(claims.Subject),
dnsNamesValidator(dnsNames),
ipAddressesValidator(ips),

View file

@ -265,14 +265,14 @@ func TestJWK_AuthorizeSign(t *testing.T) {
}
} else {
if assert.NotNil(t, got) {
assert.Len(t, 7, got)
assert.Len(t, 8, got)
_cnv := got[0]
_cnv := got[1]
cnv, ok := _cnv.(commonNameValidator)
assert.True(t, ok)
assert.Equals(t, string(cnv), "subject")
_dnv := got[1]
_dnv := got[2]
dnv, ok := _dnv.(dnsNamesValidator)
assert.True(t, ok)
if tt.name == "ok-sans" {

View file

@ -264,21 +264,19 @@ func (o *OIDC) AuthorizeSign(token string) ([]SignOption, error) {
if err != nil {
return nil, err
}
so := []SignOption{
defaultPublicKeyValidator{},
profileDefaultDuration(o.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID),
newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()),
}
// Admins should be able to authorize any SAN
if o.IsAdmin(claims.Email) {
return []SignOption{
profileDefaultDuration(o.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID),
newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()),
}, nil
return so, nil
}
return []SignOption{
emailOnlyIdentity(claims.Email),
profileDefaultDuration(o.claimer.DefaultTLSCertDuration()),
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID),
newValidityValidator(o.claimer.MinTLSCertDuration(), o.claimer.MaxTLSCertDuration()),
}, nil
return append(so, emailOnlyIdentity(claims.Email)), nil
}
// AuthorizeRenewal returns an error if the renewal is disabled.

View file

@ -286,9 +286,9 @@ func TestOIDC_AuthorizeSign(t *testing.T) {
} else {
assert.NotNil(t, got)
if tt.name == "admin" {
assert.Len(t, 3, got)
} else {
assert.Len(t, 4, got)
} else {
assert.Len(t, 5, got)
}
}
})

View file

@ -1,6 +1,8 @@
package provisioner
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
@ -10,6 +12,7 @@ import (
"github.com/pkg/errors"
"github.com/smallstep/cli/crypto/x509util"
"golang.org/x/crypto/ed25519"
)
// Options contains the options that can be passed to the Sign method.
@ -78,7 +81,7 @@ func (e emailOnlyIdentity) Valid(req *x509.CertificateRequest) error {
case len(req.EmailAddresses) == 0:
return errors.New("certificate request does not contain any email address")
case len(req.EmailAddresses) > 1:
return errors.New("certificate request does not contain too many email addresses")
return errors.New("certificate request contains too many email addresses")
case req.EmailAddresses[0] == "":
return errors.New("certificate request cannot contain an empty email address")
case req.EmailAddresses[0] != string(e):
@ -88,6 +91,23 @@ func (e emailOnlyIdentity) Valid(req *x509.CertificateRequest) error {
}
}
// defaultPublicKeyValidator validates the public key of a certificate request.
type defaultPublicKeyValidator struct{}
// Valid checks that certificate request common name matches the one configured.
func (v defaultPublicKeyValidator) Valid(req *x509.CertificateRequest) error {
switch k := req.PublicKey.(type) {
case *rsa.PublicKey:
if k.Size() < 256 {
return errors.New("rsa key in CSR must be at least 2048 bits (256 bytes)")
}
case *ecdsa.PublicKey, ed25519.PublicKey:
default:
return errors.Errorf("unrecognized public key of type '%T' in CSR", k)
}
return nil
}
// commonNameValidator validates the common name of a certificate request.
type commonNameValidator string

View file

@ -7,6 +7,12 @@ import (
"net/url"
"testing"
"time"
"github.com/pkg/errors"
"github.com/smallstep/assert"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/crypto/x509util"
stepx509 "github.com/smallstep/cli/pkg/x509"
)
func Test_emailOnlyIdentity_Valid(t *testing.T) {
@ -41,6 +47,72 @@ func Test_emailOnlyIdentity_Valid(t *testing.T) {
}
}
func Test_defaultPublicKeyValidator_Valid(t *testing.T) {
_shortRSA, err := pemutil.Read("./testdata/short-rsa.csr")
assert.FatalError(t, err)
shortRSA, ok := _shortRSA.(*x509.CertificateRequest)
assert.Fatal(t, ok)
_rsa, err := pemutil.Read("./testdata/rsa.csr")
assert.FatalError(t, err)
rsaCSR, ok := _rsa.(*x509.CertificateRequest)
assert.Fatal(t, ok)
_ecdsa, err := pemutil.Read("./testdata/ecdsa.csr")
assert.FatalError(t, err)
ecdsaCSR, ok := _ecdsa.(*x509.CertificateRequest)
assert.Fatal(t, ok)
_ed25519, err := pemutil.Read("./testdata/ed25519.csr", pemutil.WithStepCrypto())
assert.FatalError(t, err)
ed25519CSR, ok := _ed25519.(*stepx509.CertificateRequest)
assert.Fatal(t, ok)
v := defaultPublicKeyValidator{}
tests := []struct {
name string
csr *x509.CertificateRequest
err error
}{
{
"fail/unrecognized-key-type",
&x509.CertificateRequest{PublicKey: "foo"},
errors.New("unrecognized public key of type 'string' in CSR"),
},
{
"fail/rsa/too-short",
shortRSA,
errors.New("rsa key in CSR must be at least 2048 bits (256 bytes)"),
},
{
"ok/rsa",
rsaCSR,
nil,
},
{
"ok/ecdsa",
ecdsaCSR,
nil,
},
{
"ok/ed25519",
x509util.ToX509CertificateRequest(ed25519CSR),
nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := v.Valid(tt.csr); err != nil {
if assert.NotNil(t, tt.err) {
assert.HasPrefix(t, err.Error(), tt.err.Error())
}
} else {
assert.Nil(t, tt.err)
}
})
}
}
func Test_commonNameValidator_Valid(t *testing.T) {
type args struct {
req *x509.CertificateRequest

View file

@ -0,0 +1,7 @@
-----BEGIN CERTIFICATE REQUEST-----
MIHqMIGRAgEAMA4xDDAKBgNVBAMTA2ZvbzBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABKdDjTb7XIYCWC4QUq1xn5hgf3J4WpfWbd3C5frKrA4/VdQ+XfpHQIxDoHqh
jcWke0SEETc9i6HDDtWv8bXSETegITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQH
MAWCA2ZvbzAKBggqhkjOPQQDAgNIADBFAiEA1pFLT8p/YogG0o6NEEmdxzwbOzJA
A+C+DvoT91c1OcQCIGUjP3s+k6Xwdf/VukUZXTfG1lobmkZhO3vYxAjPkwA7
-----END CERTIFICATE REQUEST-----

View file

@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,54abd40e525b255542ee6161ec438721
fJvmEc5n0IG4t4FKF+ekKhpog4ods2nZjBR5KLkGH5oSGAOEADSXIRBK76Jnm/nz
Kv8ZwGqxNnoJUQyeTMlyg5OnOUAQPyNBPvoItOlD2DP32WJXgQ+NSHB2h9pcBGYG
yLWrCtzl9/P9REWskanPO4RujP27Ht62omcMO7SxxNI=
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN CERTIFICATE REQUEST-----
MIGuMGICAQAwDjEMMAoGA1UEAxMDZm9vMCowBQYDK2VwAyEA3yF/Igqb5UTp6XOq
yj+cZL9nIfjDKrUT0fMzDAHtIqqgITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQH
MAWCA2ZvbzAFBgMrZXADQQAIAx7N6ezi4NL8n0oJU8v3AmVSi0XvTuIHXUtcLGoU
OZtlO3zjWI+DgcT/ADeEKn+T8OazDxcCbTBbHiM2hIsA
-----END CERTIFICATE REQUEST-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGkMGAGCSqGSIb3DQEFDTBTMDIGCSqGSIb3DQEFDDAlBBDJ0vCXdpPyUiLlbge5
1g0jAgMBhqAwDAYIKoZIhvcNAgkAADAdBglghkgBZQMEASoEENtOknzU2eS2mlxl
73Yo/IoEQEyJS2EEx3+oYaKlFIB90e1Zkmi8da7d3r2iUlfc7faRAiKChcEvtEas
vYF2l9LEZ9DXv1Rm1uyNuSpXuddHScE=
-----END ENCRYPTED PRIVATE KEY-----

16
authority/provisioner/testdata/rsa.csr vendored Normal file
View file

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICdDCCAVwCAQAwDjEMMAoGA1UEAxMDZm9vMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA86h3t/KJylE0/aPxvF9JqPaOwSsGexuDWqDVJSOWBJi/ZqUA
Ea2Gy05ZIJkQ5GOy0bUs2JCNCVXVkfPrUkX6IvIlXpTjutjMDYyYGdgQjzpKPnOA
v3mO2a7mLMzJunws7pvrUPP7z5KDCKSAPf6VAcu/na8rGDWn1TUYR8hINK1rLQQf
OcyNWrr7yLkR84jSsrw/Qgc8NS//F4ccca1NfZecPEtxgcHjKdDQZ3SYRAfb6Dc0
jRuvoByAd3q9okOOr70gpMXgpoFVArDynaHMPK9xJ1w2p3s2/NhOYgY9f9rtcWTo
afoAcHK1jy5iQCogFUKt1bUCz5IsaYkRt+D+HQIDAQABoCEwHwYJKoZIhvcNAQkO
MRIwEDAOBgNVHREEBzAFggNmb28wDQYJKoZIhvcNAQELBQADggEBAOsv1UKwEbcY
8Fj2Pl55BjkqQG4PqSQdWJZfK0ol/GRty5XFaTgOUZyTeXOag84OGw0qM0E7kkUa
O5QwDOpnmIgg01Ywr4QM166l1iED+eOUscXJMonBAsS3JNYF1JxcDyKzIl/dt9+w
JXQ64uquuD57amOs8++ROfKW988HzXm0OnoHj8LZ1Mq2yUmxvnnfVnmMpZWo43sA
8NQs4v9dT5wLByFvBjcaWiGVZwZiwT4Q/Msskv9L0o1On0fgCJ6PjLYdblTwMHDZ
syH+X8SsUqeEmyvtiRc1XUeFbxS2hnPXJCXeyfljqwsBNGaVhBXcsV2Lg7IaloBF
/RyWqQZ44eE=
-----END CERTIFICATE REQUEST-----

30
authority/provisioner/testdata/rsa.key vendored Normal file
View file

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,e77ed7e2d2572b5a246a1c4b994190bb
29o7UA/L7OF7inTPKkwBrtd8CJdXVQs9R3oJPitmFk8SZbrLHEiEhF1C0uB3xK3s
GWQ9O7bjERM3uAvQkd7MSkUUBpyPXS9GFacd85e85d1Ubl6miTAwkFrQnT9yn6n/
Fak5JtkmdB6ObfVTioOwT1jdtGTifKg1bIhYISgwqCWhgV2fUFk6HQAAIMXTTRc+
ZK1WbunT7LimYrnN3gQ4ylm/4C8nQl3JCGpvWZaRoH91q1LLD6IwWmX0D09F37dU
X3KoKv/GvuDlV3H1dUBwxhU+GI5/lItPp9OcdLZnnr67Gs+X+do3MFT1h675TM4N
c9QEIJB6RYatLBKHCS7j8W7EbuJAFZ+MCCapP92ERmVVVsWPY+V7CDVQxM2v4X/w
7C7JYx8b4xuQbdvu9KVU5irsXg8hBx7kDb/mtWjT4+8+sseLKA4oOmI6XwVMdbow
MciGilAIaNtWwQHe0EK9E9tiQfc9OyzxdrfplRckAAehHuPGU7+iMCsigCLT3aiV
CDHmnLdTXKIvGe8faTQoJphrb9F8bobGo5D4ZqX5f6gKuPIJsfd/r0GD8VNSF/Q7
SJQMhkVyaixFB0gQbmea7sTyScdW+Qne7nLpam3ISgo+G4CAH8W88wLnuHMLmvoC
ZE3HvArSeQZ0WPHgB86AfoNRIxd6Emgb+dFyA6wPJC29nZkB8PFSrAHp0zp7KilF
fe9K2dVAUBZFhQthQIYAjJmYLCukLhxUALiqdSmQZrt6DSE33K8s5ed2KJu/60G6
lZwIzQHPXesRhwmwbkfPB8CyWM+L6osdWv8QyMdM8Wb+66zkhKWBNbm+ccMfP6Zf
1ynF/a/DRX8bf81w+nvLsCGTdxVuEVEpuzS1NclKTmYQu58Ol0RgQe2JSxL89n+A
JAHUu9g9LcTg2jNPjxeA/vusSXMZRrPqrUCYhHhcgR4mE13uyyFI/9frk0gPpKXp
/FislMydWov2JRp1ixzypMBqlFR/zF6j6m3P1g7gchwScWzrZQHD58xdRin4Udiv
OR4huswh5v2i/0KozBoUAwbvPGERnMlTaGoBMPJ5Xe/jkBJw3uC3Dhi74uyUCjqU
hMQW4RJKmuiZVfAIX0RdgeUWXPs+8pf2pXrpIiVHCAHDrxXNMC7X6/9EcBN15B88
W5/KIRngDeB2oVYrn1GfO7iLu1Rd8VFXyaVItOXq7WrL2pwm8ANhWcFDdnXf6jHW
BcKss1j8rZxOchksf+ZPXhn3QkdooD9iVONky1zLIsV5GPwMe8+yXwXznzJSbHH7
dOfhK93fZqUwx4gFULwCuWIwLTfNmQ3VzdKioGt39RFDVQb+pbR7p9jv899VjsVO
TBBpRa00fvbK1H2CMVHnwwIf82M4XypSNGR/tSD3AImZPb5RfZnznoXXCMEfYVsd
8/Ry4GHusA+zxCjCxHFtXVkb9sklewJtnUmN5mUzo/81szuigLB5IADR21IOyVBq
A4kz96Ta885Z5owhonfZp1HD53pDEbxCuuIy+fgYfjfDSAj3L/QT3ZKrdcIdYQap
PhrNRW3j38koAatTLd3+E9KqBO5BiY+T5h+Q3XesWnaXInfu5WKiiEm5hHiejA0C
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBdDCB2wIBADAOMQwwCgYDVQQDEwNmb28wgaIwDQYJKoZIhvcNAQEBBQADgZAA
MIGMAoGEAK8dks7oV6kcIFEaWna7CDGYPAE8IL7rNi+ruQ1dIYz+JtxT7OPjbCn/
t5iqni96+35iS/8CvMtEuquOMTMSWOWwlurrbTbLqCazuz/g233o8udxSxhny3cY
wHogp4cXCX6cFll6DeUnoCEuTTSIu8IBHbK48VfNw4V4gGz6cp/H93HrAgMBAAGg
ITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQHMAWCA2ZvbzANBgkqhkiG9w0BAQsF
AAOBhABCZsYM+Kgje68Z9Fjl2+cBwtQHvZDarh+cz6W1SchinZ1T0aNQvSj/otOe
ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG
2FbarLt9jN2aJLAYQPwtSeGTAZ74tLOPRPnTP6aMfFNg4XCR0uveHA==
-----END CERTIFICATE REQUEST-----

View file

@ -7,6 +7,7 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"reflect"
@ -203,6 +204,33 @@ func TestSign(t *testing.T) {
},
}
},
"fail rsa key too short": func(t *testing.T) *signTest {
shortRSAKeyPEM := `-----BEGIN CERTIFICATE REQUEST-----
MIIBdDCB2wIBADAOMQwwCgYDVQQDEwNmb28wgaIwDQYJKoZIhvcNAQEBBQADgZAA
MIGMAoGEAK8dks7oV6kcIFEaWna7CDGYPAE8IL7rNi+ruQ1dIYz+JtxT7OPjbCn/
t5iqni96+35iS/8CvMtEuquOMTMSWOWwlurrbTbLqCazuz/g233o8udxSxhny3cY
wHogp4cXCX6cFll6DeUnoCEuTTSIu8IBHbK48VfNw4V4gGz6cp/H93HrAgMBAAGg
ITAfBgkqhkiG9w0BCQ4xEjAQMA4GA1UdEQQHMAWCA2ZvbzANBgkqhkiG9w0BAQsF
AAOBhABCZsYM+Kgje68Z9Fjl2+cBwtQHvZDarh+cz6W1SchinZ1T0aNQvSj/otOe
ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG
2FbarLt9jN2aJLAYQPwtSeGTAZ74tLOPRPnTP6aMfFNg4XCR0uveHA==
-----END CERTIFICATE REQUEST-----`
block, _ := pem.Decode([]byte(shortRSAKeyPEM))
assert.FatalError(t, err)
csr, err := x509.ParseCertificateRequest(block.Bytes)
assert.FatalError(t, err)
return &signTest{
auth: a,
csr: csr,
extraOpts: extraOpts,
signOpts: signOpts,
err: &apiError{errors.New("sign: rsa key in CSR must be at least 2048 bits (256 bytes)"),
http.StatusUnauthorized,
context{"csr": csr, "signOptions": signOpts},
},
}
},
"fail store cert in db": func(t *testing.T) *signTest {
csr := getCSR(t, priv)
_a := testAuthority(t)