Create common GenerateCSR and GenerateTemplate methods for creating Certificate/CertificateRequest
This commit is contained in:
parent
4a5fe3823e
commit
1fd8cdf13e
@ -3,8 +3,6 @@ package acme
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
@ -62,15 +60,20 @@ func (a *Acme) obtainCertificate(ctx context.Context, crt *v1alpha1.Certificate)
|
||||
}
|
||||
|
||||
// generate a csr
|
||||
template := pki.GenerateCSR(commonName, altNames...)
|
||||
csr, err := x509.CreateCertificateRequest(rand.Reader, template, key)
|
||||
template, err := pki.GenerateCSR(a.issuer, crt)
|
||||
if err != nil {
|
||||
// TODO: this should probably be classed as a permanant failure
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
derBytes, err := pki.EncodeCSR(template, key)
|
||||
if err != nil {
|
||||
crt.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueError, fmt.Sprintf("Failed to generate certificate request: %v", err), false)
|
||||
return nil, nil, fmt.Errorf("error creating certificate request: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// obtain a certificate from the acme server
|
||||
certSlice, err := cl.FinalizeOrder(ctx, order.FinalizeURL, csr)
|
||||
certSlice, err := cl.FinalizeOrder(ctx, order.FinalizeURL, derBytes)
|
||||
if err != nil {
|
||||
// this handles an edge case where a certificate ends out with an order
|
||||
// that is in an invalid state.
|
||||
|
||||
@ -1,15 +1,8 @@
|
||||
package ca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
@ -31,12 +24,6 @@ const (
|
||||
messageCertIssued = "Certificate issued successfully"
|
||||
)
|
||||
|
||||
const (
|
||||
// certificateDuration of 1 year
|
||||
certificateDuration = time.Hour * 24 * 365
|
||||
defaultOrganization = "cert-manager"
|
||||
)
|
||||
|
||||
func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) ([]byte, []byte, error) {
|
||||
signeeKey, err := kube.SecretTLSKey(c.secretsLister, crt.Namespace, crt.Spec.SecretName)
|
||||
|
||||
@ -80,78 +67,15 @@ func (c *CA) obtainCertificate(crt *v1alpha1.Certificate, signeeKey interface{})
|
||||
return nil, fmt.Errorf("error getting issuer private key: %s", err.Error())
|
||||
}
|
||||
|
||||
crtPem, _, err := signCertificate(crt, signerCert, signeeKey, signerKey)
|
||||
template, err := pki.GenerateTemplate(c.issuer, crt, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
crtPem, _, err := pki.SignCertificate(template, signerCert, signeeKey, signerKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return crtPem, nil
|
||||
}
|
||||
|
||||
func createCertificateTemplate(publicKey interface{}, commonName string, altNames ...string) (*x509.Certificate, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
if len(commonName) == 0 && len(altNames) > 0 {
|
||||
commonName = altNames[0]
|
||||
}
|
||||
cert := &x509.Certificate{
|
||||
Version: 3,
|
||||
BasicConstraintsValid: true,
|
||||
SerialNumber: serialNumber,
|
||||
SignatureAlgorithm: x509.SHA256WithRSA,
|
||||
PublicKey: publicKey,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{defaultOrganization},
|
||||
CommonName: commonName,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(certificateDuration),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
DNSNames: altNames,
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// signCertificate returns a signed x509.Certificate object for the given
|
||||
// *v1alpha1.Certificate crt.
|
||||
// publicKey is the public key of the signee, and signerKey is the private
|
||||
// key of the signer.
|
||||
func signCertificate(crt *v1alpha1.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
|
||||
cn := pki.CommonNameForCertificate(crt)
|
||||
dnsNames := pki.DNSNamesForCertificate(crt)
|
||||
|
||||
template, err := createCertificateTemplate(publicKey, cn, dnsNames...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating x509 certificate template: %s", err.Error())
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
cert, err := pki.DecodeDERCertificateBytes(derBytes)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
|
||||
}
|
||||
|
||||
pemBytes := bytes.NewBuffer([]byte{})
|
||||
err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -3,9 +3,6 @@ package vault
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"path"
|
||||
@ -36,8 +33,6 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultOrganization = "cert-manager"
|
||||
|
||||
keyBitSize = 2048
|
||||
)
|
||||
|
||||
@ -68,45 +63,28 @@ func (v *Vault) obtainCertificate(ctx context.Context, crt *v1alpha1.Certificate
|
||||
return nil, nil, fmt.Errorf("error getting certificate private key: %s", err.Error())
|
||||
}
|
||||
|
||||
commonName := crt.Spec.CommonName
|
||||
altNames := crt.Spec.DNSNames
|
||||
if len(commonName) == 0 && len(altNames) == 0 {
|
||||
return nil, nil, fmt.Errorf("no domains specified on certificate")
|
||||
}
|
||||
|
||||
crtPem, err := v.signCertificate(crt, signeeKey)
|
||||
template, err := pki.GenerateCSR(v.issuer, crt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pki.EncodePKCS1PrivateKey(signeeKey), crtPem, nil
|
||||
}
|
||||
|
||||
// signCertificate returns a signed x509.Certificate object for the given
|
||||
// *v1alpha1.Certificate crt.
|
||||
func (v *Vault) signCertificate(crt *v1alpha1.Certificate, key *rsa.PrivateKey) ([]byte, error) {
|
||||
commonName := pki.CommonNameForCertificate(crt)
|
||||
altNames := pki.DNSNamesForCertificate(crt)
|
||||
|
||||
if len(commonName) == 0 && len(altNames) > 0 {
|
||||
commonName = altNames[0]
|
||||
}
|
||||
|
||||
template := pki.GenerateCSR(commonName, altNames...)
|
||||
template.Subject.Organization = []string{defaultOrganization}
|
||||
|
||||
derBytes, err := x509.CreateCertificateRequest(rand.Reader, template, key)
|
||||
derBytes, err := pki.EncodeCSR(template, signeeKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pemRequestBuf := &bytes.Buffer{}
|
||||
err = pem.Encode(pemRequestBuf, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: derBytes})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encoding certificate request: %s", err.Error())
|
||||
return nil, nil, fmt.Errorf("error encoding certificate request: %s", err.Error())
|
||||
}
|
||||
|
||||
return v.requestVaultCert(commonName, altNames, pemRequestBuf.String())
|
||||
crtBytes, err := v.requestVaultCert(template.Subject.CommonName, template.DNSNames, pemRequestBuf.Bytes())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pki.EncodePKCS1PrivateKey(signeeKey), crtBytes, nil
|
||||
}
|
||||
|
||||
func (v *Vault) initVaultClient() (*vault.Client, error) {
|
||||
@ -183,7 +161,7 @@ func (v *Vault) requestTokenWithAppRoleRef(client *vault.Client, appRole *v1alph
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (v *Vault) requestVaultCert(commonName string, altNames []string, csr string) ([]byte, error) {
|
||||
func (v *Vault) requestVaultCert(commonName string, altNames []string, csr []byte) ([]byte, error) {
|
||||
client, err := v.initVaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -195,7 +173,7 @@ func (v *Vault) requestVaultCert(commonName string, altNames []string, csr strin
|
||||
"common_name": commonName,
|
||||
"alt_names": strings.Join(altNames, ","),
|
||||
"ttl": defaultCertificateDuration.String(),
|
||||
"csr": csr,
|
||||
"csr": string(csr),
|
||||
"exclude_cn_from_sans": "true",
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
package pki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/pkg/util"
|
||||
@ -31,12 +37,106 @@ func DNSNamesForCertificate(crt *v1alpha1.Certificate) []string {
|
||||
return crt.Spec.DNSNames
|
||||
}
|
||||
|
||||
func GenerateCSR(commonName string, altNames ...string) *x509.CertificateRequest {
|
||||
template := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
},
|
||||
DNSNames: altNames,
|
||||
var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
|
||||
// TODO: allow this to be configurable
|
||||
const defaultOrganization = "cert-manager"
|
||||
|
||||
const defaultSignatureAlgorithm = x509.SHA256WithRSA
|
||||
|
||||
// default certification duration is 1 year
|
||||
const defaultNotAfter = time.Hour * 24 * 365
|
||||
|
||||
func GenerateCSR(issuer v1alpha1.GenericIssuer, crt *v1alpha1.Certificate) (*x509.CertificateRequest, error) {
|
||||
commonName := CommonNameForCertificate(crt)
|
||||
dnsNames := DNSNamesForCertificate(crt)
|
||||
if len(commonName) == 0 && len(dnsNames) == 0 {
|
||||
return nil, fmt.Errorf("no domains specified on certificate")
|
||||
}
|
||||
return &template
|
||||
|
||||
return &x509.CertificateRequest{
|
||||
Version: 3,
|
||||
SignatureAlgorithm: defaultSignatureAlgorithm,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{defaultOrganization},
|
||||
CommonName: commonName,
|
||||
},
|
||||
DNSNames: dnsNames,
|
||||
// TODO: work out how best to handle extensions/key usages here
|
||||
ExtraExtensions: []pkix.Extension{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateTemplate will create a x509.Certificate for the given Certificate resource.
|
||||
// This should create a Certificate template that is equivalent to the CertificateRequest
|
||||
// generated by GenerateCSR.
|
||||
// The PublicKey field must be populated by the caller.
|
||||
func GenerateTemplate(issuer v1alpha1.GenericIssuer, crt *v1alpha1.Certificate, serialNo *big.Int) (*x509.Certificate, error) {
|
||||
commonName := CommonNameForCertificate(crt)
|
||||
dnsNames := DNSNamesForCertificate(crt)
|
||||
if len(commonName) == 0 && len(dnsNames) == 0 {
|
||||
return nil, fmt.Errorf("no domains specified on certificate")
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
|
||||
return &x509.Certificate{
|
||||
Version: 3,
|
||||
BasicConstraintsValid: true,
|
||||
SerialNumber: serialNumber,
|
||||
SignatureAlgorithm: defaultSignatureAlgorithm,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{defaultOrganization},
|
||||
CommonName: commonName,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(defaultNotAfter),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
DNSNames: dnsNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignCertificate returns a signed x509.Certificate object for the given
|
||||
// *v1alpha1.Certificate crt.
|
||||
// publicKey is the public key of the signee, and signerKey is the private
|
||||
// key of the signer.
|
||||
func SignCertificate(template *x509.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
cert, err := DecodeDERCertificateBytes(derBytes)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
|
||||
}
|
||||
|
||||
pemBytes := bytes.NewBuffer([]byte{})
|
||||
err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func EncodeCSR(template *x509.CertificateRequest, key interface{}) ([]byte, error) {
|
||||
derBytes, err := x509.CreateCertificateRequest(rand.Reader, template, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
|
||||
}
|
||||
|
||||
return derBytes, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user