diff --git a/pkg/issuer/ca/issue.go b/pkg/issuer/ca/issue.go index b60d7a128..faf0c35ae 100644 --- a/pkg/issuer/ca/issue.go +++ b/pkg/issuer/ca/issue.go @@ -70,7 +70,7 @@ func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (*issuer.Issu } // get a copy of the CA certificate named on the Issuer - caCert, caKey, err := kube.SecretTLSKeyPair(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName) + caCerts, caKey, err := kube.SecretTLSKeyPair(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName) if err != nil { glog.Errorf("Error getting signing CA for Issuer: %v", err) return nil, err @@ -83,6 +83,8 @@ func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (*issuer.Issu return nil, err } + caCert := caCerts[0] + // sign and encode the certificate certPem, _, err := pki.SignCertificate(template, caCert, signeePublicKey, caKey) if err != nil { @@ -90,6 +92,15 @@ func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (*issuer.Issu return nil, err } + // encode the chain + // TODO: replace caCerts with caCerts[1:]? + chainPem, err := pki.EncodeX509Chain(caCerts) + if err != nil { + return nil, err + } + + certPem = append(certPem, chainPem) + // Encode output private key and CA cert ready for return keyPem, err := pki.EncodePrivateKey(signeeKey) if err != nil { @@ -98,7 +109,7 @@ func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (*issuer.Issu } // encode the CA certificate to be bundled in the output - caPem, err := pki.EncodeX509(caCert) + caPem, err := pki.EncodeX509(caCerts[0]) if err != nil { c.Recorder.Eventf(crt, corev1.EventTypeWarning, "ErrorSigning", "Error encoding certificate: %v", err) return nil, err diff --git a/pkg/util/kube/pki.go b/pkg/util/kube/pki.go index 7fe835851..97f897a05 100644 --- a/pkg/util/kube/pki.go +++ b/pkg/util/kube/pki.go @@ -56,7 +56,7 @@ func SecretTLSKey(secretLister corelisters.SecretLister, namespace, name string) return SecretTLSKeyRef(secretLister, namespace, name, api.TLSPrivateKeyKey) } -func SecretTLSCert(secretLister corelisters.SecretLister, namespace, name string) (*x509.Certificate, error) { +func SecretTLSCertChain(secretLister corelisters.SecretLister, namespace, name string) ([]*x509.Certificate, error) { secret, err := secretLister.Secrets(namespace).Get(name) if err != nil { return nil, err @@ -64,9 +64,9 @@ func SecretTLSCert(secretLister corelisters.SecretLister, namespace, name string certBytes, ok := secret.Data[api.TLSCertKey] if !ok { - return nil, fmt.Errorf("no data for %q in secret '%s/%s'", api.TLSCertKey, namespace, name) + return nil, errors.NewInvalidData("no data for %q in secret '%s/%s'", api.TLSCertKey, namespace, name) } - cert, err := pki.DecodeX509CertificateBytes(certBytes) + cert, err := pki.DecodeX509CertificateChainBytes(certBytes) if err != nil { return cert, errors.NewInvalidData(err.Error()) } @@ -74,7 +74,7 @@ func SecretTLSCert(secretLister corelisters.SecretLister, namespace, name string return cert, nil } -func SecretTLSKeyPair(secretLister corelisters.SecretLister, namespace, name string) (*x509.Certificate, crypto.Signer, error) { +func SecretTLSKeyPair(secretLister corelisters.SecretLister, namespace, name string) ([]*x509.Certificate, crypto.Signer, error) { secret, err := secretLister.Secrets(namespace).Get(name) if err != nil { return nil, nil, err @@ -93,10 +93,19 @@ func SecretTLSKeyPair(secretLister corelisters.SecretLister, namespace, name str if !ok { return nil, key, errors.NewInvalidData("no certificate data for %q in secret '%s/%s'", api.TLSCertKey, namespace, name) } - cert, err := pki.DecodeX509CertificateBytes(certBytes) + cert, err := pki.DecodeX509CertificateChainBytes(certBytes) if err != nil { return nil, key, errors.NewInvalidData(err.Error()) } return cert, key, nil } + +func SecretTLSCert(secretLister corelisters.SecretLister, namespace, name string) (*x509.Certificate, error) { + certs, err := SecretTLSCertChain(secretLister, namespace, name) + if err != nil { + return nil, err + } + + return certs[0], nil +} diff --git a/pkg/util/pki/csr.go b/pkg/util/pki/csr.go index b5e3e532b..8a7597342 100644 --- a/pkg/util/pki/csr.go +++ b/pkg/util/pki/csr.go @@ -193,16 +193,6 @@ func SignCertificate(template *x509.Certificate, issuerCert *x509.Certificate, p return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error()) } - // don't bundle the CA for selfsigned certificates - // TODO: better comparison method here? for now we can just compare pointers. - if issuerCert != template { - // bundle the CA - err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: issuerCert.Raw}) - if err != nil { - return nil, nil, fmt.Errorf("error encoding issuer cetificate PEM: %s", err.Error()) - } - } - return pemBytes.Bytes(), cert, err } @@ -228,6 +218,23 @@ func EncodeX509(cert *x509.Certificate) ([]byte, error) { return caPem.Bytes(), nil } +// EncodeX509Chain will encode an *x509.Certificate chain into PEM format. +func EncodeX509Chain(certs []*x509.Certificate) ([]byte, error) { + caPem := bytes.NewBuffer([]byte{}) + for _, cert := range certs { + if bytes.Equal(cert.RawIssuer, cert.RawSubject) { + // Don't include self-signed certificate + continue + } + err := pem.Encode(caPem, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) + if err != nil { + return nil, err + } + } + + return caPem.Bytes(), nil +} + // SignatureAlgorithm will determine the appropriate signature algorithm for // the given certificate. // Adapted from https://github.com/cloudflare/cfssl/blob/master/csr/csr.go#L102 diff --git a/pkg/util/pki/parse.go b/pkg/util/pki/parse.go index 54dadcb1a..4f48f6265 100644 --- a/pkg/util/pki/parse.go +++ b/pkg/util/pki/parse.go @@ -78,18 +78,40 @@ func DecodePKCS1PrivateKeyBytes(keyBytes []byte) (*rsa.PrivateKey, error) { return key, nil } -// DecodeX509CertificateBytes will decode a PEM encoded x509 Certificate. -func DecodeX509CertificateBytes(certBytes []byte) (*x509.Certificate, error) { - // decode the tls certificate pem - block, _ := pem.Decode(certBytes) - if block == nil { - return nil, errors.NewInvalidData("error decoding cert PEM block") - } - // parse the tls certificate - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, errors.NewInvalidData("error parsing TLS certificate: %s", err.Error()) +// DecodeX509CertificateChainBytes will decode a PEM encoded x509 Certificate chain. +func DecodeX509CertificateChainBytes(certBytes []byte) ([]*x509.Certificate, error) { + certs := []*x509.Certificate{} + + var block *pem.Block + + for { + // decode the tls certificate pem + block, certBytes = pem.Decode(certBytes) + if block == nil { + break + } + + // parse the tls certificate + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, errors.NewInvalidData("error parsing TLS certificate: %s", err.Error()) + } + certs = append(certs, cert) } - return cert, nil + if len(certs) == 0 { + return nil, errors.NewInvalidData("error decoding cert PEM block") + } + + return certs, nil +} + +// DecodeX509CertificateBytes will decode a PEM encoded x509 Certificate. +func DecodeX509CertificateBytes(certBytes []byte) (*x509.Certificate, error) { + certs, err := DecodeX509CertificateChainBytes(certBytes) + if err != nil { + return nil, err + } + + return certs[0], nil }