forked from TrueCloudLab/certificates
Change api of functions Authority.Sign, Authority.Renew
Returns certificate chain instead of 2 members. Implements #126
This commit is contained in:
parent
e2858e17b0
commit
bc6074f596
10 changed files with 109 additions and 61 deletions
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// SignAuthority is the interface implemented by a CA authority.
|
// SignAuthority is the interface implemented by a CA authority.
|
||||||
type SignAuthority interface {
|
type SignAuthority interface {
|
||||||
Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error)
|
Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||||
LoadProvisionerByID(string) (provisioner.Interface, error)
|
LoadProvisionerByID(string) (provisioner.Interface, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ func (o *order) finalize(db nosql.DB, csr *x509.CertificateRequest, auth SignAut
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and store a new certificate.
|
// Create and store a new certificate.
|
||||||
leaf, inter, err := auth.Sign(csr, provisioner.Options{
|
certChain, err := auth.Sign(csr, provisioner.Options{
|
||||||
NotBefore: provisioner.NewTimeDuration(o.NotBefore),
|
NotBefore: provisioner.NewTimeDuration(o.NotBefore),
|
||||||
NotAfter: provisioner.NewTimeDuration(o.NotAfter),
|
NotAfter: provisioner.NewTimeDuration(o.NotAfter),
|
||||||
}, signOps...)
|
}, signOps...)
|
||||||
|
@ -285,8 +285,8 @@ func (o *order) finalize(db nosql.DB, csr *x509.CertificateRequest, auth SignAut
|
||||||
cert, err := newCert(db, CertOptions{
|
cert, err := newCert(db, CertOptions{
|
||||||
AccountID: o.AccountID,
|
AccountID: o.AccountID,
|
||||||
OrderID: o.ID,
|
OrderID: o.ID,
|
||||||
Leaf: leaf,
|
Leaf: certChain[0],
|
||||||
Intermediates: []*x509.Certificate{inter},
|
Intermediates: certChain[1:],
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -789,19 +789,19 @@ func TestOrderUpdateStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockSignAuth struct {
|
type mockSignAuth struct {
|
||||||
sign func(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error)
|
sign func(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||||
loadProvisionerByID func(string) (provisioner.Interface, error)
|
loadProvisionerByID func(string) (provisioner.Interface, error)
|
||||||
ret1, ret2 interface{}
|
ret1, ret2 interface{}
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error) {
|
func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
if m.sign != nil {
|
if m.sign != nil {
|
||||||
return m.sign(csr, signOpts, extraOpts...)
|
return m.sign(csr, signOpts, extraOpts...)
|
||||||
} else if m.err != nil {
|
} else if m.err != nil {
|
||||||
return nil, nil, m.err
|
return nil, m.err
|
||||||
}
|
}
|
||||||
return m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate), m.err
|
return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockSignAuth) LoadProvisionerByID(id string) (provisioner.Interface, error) {
|
func (m *mockSignAuth) LoadProvisionerByID(id string) (provisioner.Interface, error) {
|
||||||
|
@ -1082,9 +1082,9 @@ func TestOrderFinalize(t *testing.T) {
|
||||||
res: clone,
|
res: clone,
|
||||||
csr: csr,
|
csr: csr,
|
||||||
sa: &mockSignAuth{
|
sa: &mockSignAuth{
|
||||||
sign: func(csr *x509.CertificateRequest, pops provisioner.Options, signOps ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error) {
|
sign: func(csr *x509.CertificateRequest, pops provisioner.Options, signOps ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
assert.Equals(t, len(signOps), 4)
|
assert.Equals(t, len(signOps), 4)
|
||||||
return crt, inter, nil
|
return []*x509.Certificate{crt, inter}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
db: &db.MockNoSQLDB{
|
||||||
|
|
54
api/api.go
54
api/api.go
|
@ -33,8 +33,8 @@ type Authority interface {
|
||||||
AuthorizeSign(ott string) ([]provisioner.SignOption, error)
|
AuthorizeSign(ott string) ([]provisioner.SignOption, error)
|
||||||
GetTLSOptions() *tlsutil.TLSOptions
|
GetTLSOptions() *tlsutil.TLSOptions
|
||||||
Root(shasum string) (*x509.Certificate, error)
|
Root(shasum string) (*x509.Certificate, error)
|
||||||
Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error)
|
Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||||
Renew(peer *x509.Certificate) (*x509.Certificate, *x509.Certificate, error)
|
Renew(peer *x509.Certificate) ([]*x509.Certificate, error)
|
||||||
LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, error)
|
LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, error)
|
||||||
LoadProvisionerByID(string) (provisioner.Interface, error)
|
LoadProvisionerByID(string) (provisioner.Interface, error)
|
||||||
GetProvisioners(cursor string, limit int) (provisioner.List, string, error)
|
GetProvisioners(cursor string, limit int) (provisioner.List, string, error)
|
||||||
|
@ -211,10 +211,11 @@ func (s *SignRequest) Validate() error {
|
||||||
|
|
||||||
// SignResponse is the response object of the certificate signature request.
|
// SignResponse is the response object of the certificate signature request.
|
||||||
type SignResponse struct {
|
type SignResponse struct {
|
||||||
ServerPEM Certificate `json:"crt"`
|
ServerPEM Certificate `json:"crt"`
|
||||||
CaPEM Certificate `json:"ca"`
|
CaPEM Certificate `json:"ca"`
|
||||||
TLSOptions *tlsutil.TLSOptions `json:"tlsOptions,omitempty"`
|
CertChainPEM []Certificate `json:"certChain"`
|
||||||
TLS *tls.ConnectionState `json:"-"`
|
TLSOptions *tlsutil.TLSOptions `json:"tlsOptions,omitempty"`
|
||||||
|
TLS *tls.ConnectionState `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootsResponse is the response object of the roots request.
|
// RootsResponse is the response object of the roots request.
|
||||||
|
@ -275,6 +276,14 @@ func (h *caHandler) Root(w http.ResponseWriter, r *http.Request) {
|
||||||
JSON(w, &RootResponse{RootPEM: Certificate{cert}})
|
JSON(w, &RootResponse{RootPEM: Certificate{cert}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func certChainToPEM(certChain []*x509.Certificate) []Certificate {
|
||||||
|
certChainPEM := make([]Certificate, 0, len(certChain))
|
||||||
|
for _, c := range certChain {
|
||||||
|
certChainPEM = append(certChainPEM, Certificate{c})
|
||||||
|
}
|
||||||
|
return certChainPEM
|
||||||
|
}
|
||||||
|
|
||||||
// Sign is an HTTP handler that reads a certificate request and an
|
// Sign is an HTTP handler that reads a certificate request and an
|
||||||
// one-time-token (ott) from the body and creates a new certificate with the
|
// one-time-token (ott) from the body and creates a new certificate with the
|
||||||
// information in the certificate request.
|
// information in the certificate request.
|
||||||
|
@ -302,17 +311,22 @@ func (h *caHandler) Sign(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, root, err := h.Authority.Sign(body.CsrPEM.CertificateRequest, opts, signOpts...)
|
certChain, err := h.Authority.Sign(body.CsrPEM.CertificateRequest, opts, signOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteError(w, Forbidden(err))
|
WriteError(w, Forbidden(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
certChainPEM := certChainToPEM(certChain)
|
||||||
logCertificate(w, cert)
|
var caPEM Certificate
|
||||||
|
if len(certChainPEM) > 0 {
|
||||||
|
caPEM = certChainPEM[1]
|
||||||
|
}
|
||||||
|
logCertificate(w, certChain[0])
|
||||||
JSONStatus(w, &SignResponse{
|
JSONStatus(w, &SignResponse{
|
||||||
ServerPEM: Certificate{cert},
|
ServerPEM: certChainPEM[0],
|
||||||
CaPEM: Certificate{root},
|
CaPEM: caPEM,
|
||||||
TLSOptions: h.Authority.GetTLSOptions(),
|
CertChainPEM: certChainPEM,
|
||||||
|
TLSOptions: h.Authority.GetTLSOptions(),
|
||||||
}, http.StatusCreated)
|
}, http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,17 +338,23 @@ func (h *caHandler) Renew(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, root, err := h.Authority.Renew(r.TLS.PeerCertificates[0])
|
certChain, err := h.Authority.Renew(r.TLS.PeerCertificates[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteError(w, Forbidden(err))
|
WriteError(w, Forbidden(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
certChainPEM := certChainToPEM(certChain)
|
||||||
|
var caPEM Certificate
|
||||||
|
if len(certChainPEM) > 0 {
|
||||||
|
caPEM = certChainPEM[1]
|
||||||
|
}
|
||||||
|
|
||||||
logCertificate(w, cert)
|
logCertificate(w, certChain[0])
|
||||||
JSONStatus(w, &SignResponse{
|
JSONStatus(w, &SignResponse{
|
||||||
ServerPEM: Certificate{cert},
|
ServerPEM: certChainPEM[0],
|
||||||
CaPEM: Certificate{root},
|
CaPEM: caPEM,
|
||||||
TLSOptions: h.Authority.GetTLSOptions(),
|
CertChainPEM: certChainPEM,
|
||||||
|
TLSOptions: h.Authority.GetTLSOptions(),
|
||||||
}, http.StatusCreated)
|
}, http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -501,10 +501,10 @@ type mockAuthority struct {
|
||||||
authorizeSign func(ott string) ([]provisioner.SignOption, error)
|
authorizeSign func(ott string) ([]provisioner.SignOption, error)
|
||||||
getTLSOptions func() *tlsutil.TLSOptions
|
getTLSOptions func() *tlsutil.TLSOptions
|
||||||
root func(shasum string) (*x509.Certificate, error)
|
root func(shasum string) (*x509.Certificate, error)
|
||||||
sign func(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error)
|
sign func(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error)
|
||||||
signSSH func(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
signSSH func(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
||||||
signSSHAddUser func(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
signSSHAddUser func(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
||||||
renew func(cert *x509.Certificate) (*x509.Certificate, *x509.Certificate, error)
|
renew func(cert *x509.Certificate) ([]*x509.Certificate, error)
|
||||||
loadProvisionerByCertificate func(cert *x509.Certificate) (provisioner.Interface, error)
|
loadProvisionerByCertificate func(cert *x509.Certificate) (provisioner.Interface, error)
|
||||||
loadProvisionerByID func(provID string) (provisioner.Interface, error)
|
loadProvisionerByID func(provID string) (provisioner.Interface, error)
|
||||||
getProvisioners func(nextCursor string, limit int) (provisioner.List, string, error)
|
getProvisioners func(nextCursor string, limit int) (provisioner.List, string, error)
|
||||||
|
@ -540,11 +540,11 @@ func (m *mockAuthority) Root(shasum string) (*x509.Certificate, error) {
|
||||||
return m.ret1.(*x509.Certificate), m.err
|
return m.ret1.(*x509.Certificate), m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAuthority) Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error) {
|
func (m *mockAuthority) Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
if m.sign != nil {
|
if m.sign != nil {
|
||||||
return m.sign(cr, opts, signOpts...)
|
return m.sign(cr, opts, signOpts...)
|
||||||
}
|
}
|
||||||
return m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate), m.err
|
return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAuthority) SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
func (m *mockAuthority) SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
||||||
|
@ -561,11 +561,11 @@ func (m *mockAuthority) SignSSHAddUser(key ssh.PublicKey, cert *ssh.Certificate)
|
||||||
return m.ret1.(*ssh.Certificate), m.err
|
return m.ret1.(*ssh.Certificate), m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAuthority) Renew(cert *x509.Certificate) (*x509.Certificate, *x509.Certificate, error) {
|
func (m *mockAuthority) Renew(cert *x509.Certificate) ([]*x509.Certificate, error) {
|
||||||
if m.renew != nil {
|
if m.renew != nil {
|
||||||
return m.renew(cert)
|
return m.renew(cert)
|
||||||
}
|
}
|
||||||
return m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate), m.err
|
return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAuthority) GetProvisioners(nextCursor string, limit int) (provisioner.List, string, error) {
|
func (m *mockAuthority) GetProvisioners(nextCursor string, limit int) (provisioner.List, string, error) {
|
||||||
|
@ -724,8 +724,8 @@ func Test_caHandler_Sign(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected1 := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"}`)
|
expected1 := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`)
|
||||||
expected2 := []byte(`{"crt":"` + strings.Replace(stepCertPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"}`)
|
expected2 := []byte(`{"crt":"` + strings.Replace(stepCertPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(stepCertPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -798,7 +798,7 @@ func Test_caHandler_Renew(t *testing.T) {
|
||||||
{"renew error", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden},
|
{"renew error", cs, nil, nil, fmt.Errorf("an error"), http.StatusForbidden},
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"}`)
|
expected := []byte(`{"crt":"` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","ca":"` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n","certChain":["` + strings.Replace(certPEM, "\n", `\n`, -1) + `\n","` + strings.Replace(rootPEM, "\n", `\n`, -1) + `\n"]}`)
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ func withDefaultASN1DN(def *x509util.ASN1DN) x509util.WithOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign creates a signed certificate from a certificate signing request.
|
// Sign creates a signed certificate from a certificate signing request.
|
||||||
func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error) {
|
func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
var (
|
var (
|
||||||
errContext = apiCtx{"csr": csr, "signOptions": signOpts}
|
errContext = apiCtx{"csr": csr, "signOptions": signOpts}
|
||||||
mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)}
|
mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)}
|
||||||
|
@ -69,66 +69,66 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
|
||||||
certValidators = append(certValidators, k)
|
certValidators = append(certValidators, k)
|
||||||
case provisioner.CertificateRequestValidator:
|
case provisioner.CertificateRequestValidator:
|
||||||
if err := k.Valid(csr); err != nil {
|
if err := k.Valid(csr); err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign"), http.StatusUnauthorized, errContext}
|
return nil, &apiError{errors.Wrap(err, "sign"), http.StatusUnauthorized, errContext}
|
||||||
}
|
}
|
||||||
case provisioner.ProfileModifier:
|
case provisioner.ProfileModifier:
|
||||||
mods = append(mods, k.Option(signOpts))
|
mods = append(mods, k.Option(signOpts))
|
||||||
default:
|
default:
|
||||||
return nil, nil, &apiError{errors.Errorf("sign: invalid extra option type %T", k),
|
return nil, &apiError{errors.Errorf("sign: invalid extra option type %T", k),
|
||||||
http.StatusInternalServerError, errContext}
|
http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := csr.CheckSignature(); err != nil {
|
if err := csr.CheckSignature(); err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign: invalid certificate request"),
|
return nil, &apiError{errors.Wrap(err, "sign: invalid certificate request"),
|
||||||
http.StatusBadRequest, errContext}
|
http.StatusBadRequest, errContext}
|
||||||
}
|
}
|
||||||
|
|
||||||
leaf, err := x509util.NewLeafProfileWithCSR(csr, issIdentity.Crt, issIdentity.Key, mods...)
|
leaf, err := x509util.NewLeafProfileWithCSR(csr, issIdentity.Crt, issIdentity.Key, mods...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrapf(err, "sign"), http.StatusInternalServerError, errContext}
|
return nil, &apiError{errors.Wrapf(err, "sign"), http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range certValidators {
|
for _, v := range certValidators {
|
||||||
if err := v.Valid(leaf.Subject()); err != nil {
|
if err := v.Valid(leaf.Subject()); err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign"), http.StatusUnauthorized, errContext}
|
return nil, &apiError{errors.Wrap(err, "sign"), http.StatusUnauthorized, errContext}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crtBytes, err := leaf.CreateCertificate()
|
crtBytes, err := leaf.CreateCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign: error creating new leaf certificate"),
|
return nil, &apiError{errors.Wrap(err, "sign: error creating new leaf certificate"),
|
||||||
http.StatusInternalServerError, errContext}
|
http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
|
|
||||||
serverCert, err := x509.ParseCertificate(crtBytes)
|
serverCert, err := x509.ParseCertificate(crtBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign: error parsing new leaf certificate"),
|
return nil, &apiError{errors.Wrap(err, "sign: error parsing new leaf certificate"),
|
||||||
http.StatusInternalServerError, errContext}
|
http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
|
|
||||||
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw)
|
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"),
|
return nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"),
|
||||||
http.StatusInternalServerError, errContext}
|
http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.db.StoreCertificate(serverCert); err != nil {
|
if err = a.db.StoreCertificate(serverCert); err != nil {
|
||||||
if err != db.ErrNotImplemented {
|
if err != db.ErrNotImplemented {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "sign: error storing certificate in db"),
|
return nil, &apiError{errors.Wrap(err, "sign: error storing certificate in db"),
|
||||||
http.StatusInternalServerError, errContext}
|
http.StatusInternalServerError, errContext}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverCert, caCert, nil
|
return []*x509.Certificate{serverCert, caCert}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renew creates a new Certificate identical to the old certificate, except
|
// Renew creates a new Certificate identical to the old certificate, except
|
||||||
// with a validity window that begins 'now'.
|
// with a validity window that begins 'now'.
|
||||||
func (a *Authority) Renew(oldCert *x509.Certificate) (*x509.Certificate, *x509.Certificate, error) {
|
func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error) {
|
||||||
// Check step provisioner extensions
|
// Check step provisioner extensions
|
||||||
if err := a.authorizeRenewal(oldCert); err != nil {
|
if err := a.authorizeRenewal(oldCert); err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issuer
|
// Issuer
|
||||||
|
@ -181,26 +181,26 @@ func (a *Authority) Renew(oldCert *x509.Certificate) (*x509.Certificate, *x509.C
|
||||||
leaf, err := x509util.NewLeafProfileWithTemplate(newCert,
|
leaf, err := x509util.NewLeafProfileWithTemplate(newCert,
|
||||||
issIdentity.Crt, issIdentity.Key)
|
issIdentity.Crt, issIdentity.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{err, http.StatusInternalServerError, apiCtx{}}
|
return nil, &apiError{err, http.StatusInternalServerError, apiCtx{}}
|
||||||
}
|
}
|
||||||
crtBytes, err := leaf.CreateCertificate()
|
crtBytes, err := leaf.CreateCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "error renewing certificate from existing server certificate"),
|
return nil, &apiError{errors.Wrap(err, "error renewing certificate from existing server certificate"),
|
||||||
http.StatusInternalServerError, apiCtx{}}
|
http.StatusInternalServerError, apiCtx{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
serverCert, err := x509.ParseCertificate(crtBytes)
|
serverCert, err := x509.ParseCertificate(crtBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "error parsing new server certificate"),
|
return nil, &apiError{errors.Wrap(err, "error parsing new server certificate"),
|
||||||
http.StatusInternalServerError, apiCtx{}}
|
http.StatusInternalServerError, apiCtx{}}
|
||||||
}
|
}
|
||||||
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw)
|
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &apiError{errors.Wrap(err, "error parsing intermediate certificate"),
|
return nil, &apiError{errors.Wrap(err, "error parsing intermediate certificate"),
|
||||||
http.StatusInternalServerError, apiCtx{}}
|
http.StatusInternalServerError, apiCtx{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverCert, caCert, nil
|
return []*x509.Certificate{serverCert, caCert}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeOptions are the options for the Revoke API.
|
// RevokeOptions are the options for the Revoke API.
|
||||||
|
|
|
@ -276,7 +276,7 @@ ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tc := genTestCase(t)
|
tc := genTestCase(t)
|
||||||
|
|
||||||
leaf, intermediate, err := tc.auth.Sign(tc.csr, tc.signOpts, tc.extraOpts...)
|
certChain, err := tc.auth.Sign(tc.csr, tc.signOpts, tc.extraOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if assert.NotNil(t, tc.err) {
|
if assert.NotNil(t, tc.err) {
|
||||||
switch v := err.(type) {
|
switch v := err.(type) {
|
||||||
|
@ -289,6 +289,8 @@ ttnEF4Rq8zqzr4fbv+AF451Mx36AkfgZr9XWGzxidrH+fBCNWXWNR+ymhrL6UFTG
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
leaf := certChain[0]
|
||||||
|
intermediate := certChain[1]
|
||||||
if assert.Nil(t, tc.err) {
|
if assert.Nil(t, tc.err) {
|
||||||
assert.Equals(t, leaf.NotBefore, signOpts.NotBefore.Time().Truncate(time.Second))
|
assert.Equals(t, leaf.NotBefore, signOpts.NotBefore.Time().Truncate(time.Second))
|
||||||
assert.Equals(t, leaf.NotAfter, signOpts.NotAfter.Time().Truncate(time.Second))
|
assert.Equals(t, leaf.NotAfter, signOpts.NotAfter.Time().Truncate(time.Second))
|
||||||
|
@ -453,11 +455,11 @@ func TestRenew(t *testing.T) {
|
||||||
tc, err := genTestCase()
|
tc, err := genTestCase()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
var leaf, intermediate *x509.Certificate
|
var certChain []*x509.Certificate
|
||||||
if tc.auth != nil {
|
if tc.auth != nil {
|
||||||
leaf, intermediate, err = tc.auth.Renew(tc.crt)
|
certChain, err = tc.auth.Renew(tc.crt)
|
||||||
} else {
|
} else {
|
||||||
leaf, intermediate, err = a.Renew(tc.crt)
|
certChain, err = a.Renew(tc.crt)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if assert.NotNil(t, tc.err) {
|
if assert.NotNil(t, tc.err) {
|
||||||
|
@ -471,6 +473,8 @@ func TestRenew(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
leaf := certChain[0]
|
||||||
|
intermediate := certChain[1]
|
||||||
if assert.Nil(t, tc.err) {
|
if assert.Nil(t, tc.err) {
|
||||||
assert.Equals(t, leaf.NotAfter.Sub(leaf.NotBefore), tc.crt.NotAfter.Sub(crt.NotBefore))
|
assert.Equals(t, leaf.NotAfter.Sub(leaf.NotBefore), tc.crt.NotAfter.Sub(crt.NotBefore))
|
||||||
|
|
||||||
|
|
|
@ -303,7 +303,7 @@ func TestBootstrapClient(t *testing.T) {
|
||||||
t.Errorf("BootstrapClient() error reading response: %v", err)
|
t.Errorf("BootstrapClient() error reading response: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if renewal.CaPEM.Certificate == nil || renewal.ServerPEM.Certificate == nil {
|
if renewal.CaPEM.Certificate == nil || renewal.ServerPEM.Certificate == nil || len(renewal.CertChainPEM) == 0 {
|
||||||
t.Errorf("BootstrapClient() invalid renewal response: %v", renewal)
|
t.Errorf("BootstrapClient() invalid renewal response: %v", renewal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +375,7 @@ func TestBootstrapClientServerRotation(t *testing.T) {
|
||||||
if err := readJSON(resp.Body, &renew); err != nil {
|
if err := readJSON(resp.Body, &renew); err != nil {
|
||||||
return errors.Wrap(err, "client.Post() error reading response")
|
return errors.Wrap(err, "client.Post() error reading response")
|
||||||
}
|
}
|
||||||
if renew.ServerPEM.Certificate == nil || renew.CaPEM.Certificate == nil {
|
if renew.ServerPEM.Certificate == nil || renew.CaPEM.Certificate == nil || len(renew.CertChainPEM) == 0 {
|
||||||
return errors.New("client.Post() unexpected response found")
|
return errors.New("client.Post() unexpected response found")
|
||||||
}
|
}
|
||||||
// test with bootstrap server
|
// test with bootstrap server
|
||||||
|
@ -492,7 +492,7 @@ func TestBootstrapClientServerFederation(t *testing.T) {
|
||||||
if err := readJSON(resp.Body, &renew); err != nil {
|
if err := readJSON(resp.Body, &renew); err != nil {
|
||||||
return errors.Wrap(err, "client.Post() error reading response")
|
return errors.Wrap(err, "client.Post() error reading response")
|
||||||
}
|
}
|
||||||
if renew.ServerPEM.Certificate == nil || renew.CaPEM.Certificate == nil {
|
if renew.ServerPEM.Certificate == nil || renew.CaPEM.Certificate == nil || len(renew.CertChainPEM) == 0 {
|
||||||
return errors.New("client.Post() unexpected response found")
|
return errors.New("client.Post() unexpected response found")
|
||||||
}
|
}
|
||||||
// test with bootstrap server
|
// test with bootstrap server
|
||||||
|
|
|
@ -253,6 +253,10 @@ func TestClient_Sign(t *testing.T) {
|
||||||
ok := &api.SignResponse{
|
ok := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: parseCertificate(certPEM)},
|
||||||
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
request := &api.SignRequest{
|
request := &api.SignRequest{
|
||||||
CsrPEM: api.CertificateRequest{CertificateRequest: parseCertificateRequest(csrPEM)},
|
CsrPEM: api.CertificateRequest{CertificateRequest: parseCertificateRequest(csrPEM)},
|
||||||
|
@ -406,6 +410,10 @@ func TestClient_Renew(t *testing.T) {
|
||||||
ok := &api.SignResponse{
|
ok := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: parseCertificate(certPEM)},
|
||||||
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
|
@ -417,6 +417,10 @@ func TestCertificate(t *testing.T) {
|
||||||
ok := &api.SignResponse{
|
ok := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: cert},
|
ServerPEM: api.Certificate{Certificate: cert},
|
||||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: cert},
|
||||||
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -446,6 +450,10 @@ func TestIntermediateCertificate(t *testing.T) {
|
||||||
ok := &api.SignResponse{
|
ok := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||||
CaPEM: api.Certificate{Certificate: intermediate},
|
CaPEM: api.Certificate{Certificate: intermediate},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: parseCertificate(certPEM)},
|
||||||
|
{Certificate: intermediate},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -475,6 +483,10 @@ func TestRootCertificateCertificate(t *testing.T) {
|
||||||
ok := &api.SignResponse{
|
ok := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: parseCertificate(certPEM)},
|
||||||
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
|
},
|
||||||
TLS: &tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{
|
TLS: &tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{
|
||||||
{root, root},
|
{root, root},
|
||||||
}},
|
}},
|
||||||
|
@ -482,6 +494,10 @@ func TestRootCertificateCertificate(t *testing.T) {
|
||||||
noTLS := &api.SignResponse{
|
noTLS := &api.SignResponse{
|
||||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
|
CertChainPEM: []api.Certificate{
|
||||||
|
{Certificate: parseCertificate(certPEM)},
|
||||||
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
Loading…
Reference in a new issue