From aa7ad4fea3f727ed5270380412e5b9b743a1cca0 Mon Sep 17 00:00:00 2001 From: Matthew Sykes Date: Wed, 3 Feb 2021 14:58:53 -0500 Subject: [PATCH] Set SKI, support multi hosts, add Signer to CA Signed-off-by: Matthew Sykes --- common/crypto/tlsgen/ca.go | 20 ++++++++++++++------ common/crypto/tlsgen/ca_test.go | 6 ++++++ common/crypto/tlsgen/key.go | 21 ++++++++++++++++----- common/crypto/tlsgen/key_test.go | 2 +- internal/cryptogen/ca/ca.go | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/common/crypto/tlsgen/ca.go b/common/crypto/tlsgen/ca.go index 8e9d29fdc22..7ee89d34dc6 100644 --- a/common/crypto/tlsgen/ca.go +++ b/common/crypto/tlsgen/ca.go @@ -40,7 +40,10 @@ type CA interface { // with a given custom SAN. // The certificate is signed by the CA. // Returns nil, error in case of failure - NewServerCertKeyPair(host string) (*CertKeyPair, error) + NewServerCertKeyPair(hosts ...string) (*CertKeyPair, error) + + // Signer returns a crypto.Signer that signs with the CA's private key. + Signer() crypto.Signer } type ca struct { @@ -50,7 +53,7 @@ type ca struct { func NewCA() (CA, error) { c := &ca{} var err error - c.caCert, err = newCertKeyPair(true, false, "", nil, nil) + c.caCert, err = newCertKeyPair(true, false, nil, nil) if err != nil { return nil, err } @@ -60,7 +63,7 @@ func NewCA() (CA, error) { func (c *ca) NewIntermediateCA() (CA, error) { intermediateCA := &ca{} var err error - intermediateCA.caCert, err = newCertKeyPair(true, false, "", c.caCert.Signer, c.caCert.TLSCert) + intermediateCA.caCert, err = newCertKeyPair(true, false, c.caCert.Signer, c.caCert.TLSCert) if err != nil { return nil, err } @@ -76,16 +79,21 @@ func (c *ca) CertBytes() []byte { // or nil, error in case of failure // The certificate is signed by the CA and is used as a client TLS certificate func (c *ca) NewClientCertKeyPair() (*CertKeyPair, error) { - return newCertKeyPair(false, false, "", c.caCert.Signer, c.caCert.TLSCert) + return newCertKeyPair(false, false, c.caCert.Signer, c.caCert.TLSCert) } // newServerCertKeyPair returns a certificate and private key pair and nil, // or nil, error in case of failure // The certificate is signed by the CA and is used as a server TLS certificate -func (c *ca) NewServerCertKeyPair(host string) (*CertKeyPair, error) { - keypair, err := newCertKeyPair(false, true, host, c.caCert.Signer, c.caCert.TLSCert) +func (c *ca) NewServerCertKeyPair(hosts ...string) (*CertKeyPair, error) { + keypair, err := newCertKeyPair(false, true, c.caCert.Signer, c.caCert.TLSCert, hosts...) if err != nil { return nil, err } return keypair, nil } + +// Signer returns a crypto.Signer that signs with the CA's private key. +func (c *ca) Signer() crypto.Signer { + return c.caCert.Signer +} diff --git a/common/crypto/tlsgen/ca_test.go b/common/crypto/tlsgen/ca_test.go index e19fcf7b5c4..4c127d09313 100644 --- a/common/crypto/tlsgen/ca_test.go +++ b/common/crypto/tlsgen/ca_test.go @@ -82,3 +82,9 @@ func TestTLSCA(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "context deadline exceeded") } + +func TestTLSCASigner(t *testing.T) { + tlsCA, err := NewCA() + assert.NoError(t, err) + assert.Equal(t, tlsCA.(*ca).caCert.Signer, tlsCA.Signer()) +} diff --git a/common/crypto/tlsgen/key.go b/common/crypto/tlsgen/key.go index a57b0bcd999..ae1ea107bc9 100644 --- a/common/crypto/tlsgen/key.go +++ b/common/crypto/tlsgen/key.go @@ -11,6 +11,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -47,7 +48,7 @@ func newCertTemplate() (x509.Certificate, error) { }, nil } -func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Signer, parent *x509.Certificate) (*CertKeyPair, error) { +func newCertKeyPair(isCA bool, isServer bool, certSigner crypto.Signer, parent *x509.Certificate, hosts ...string) (*CertKeyPair, error) { privateKey, privBytes, err := newPrivKey() if err != nil { return nil, err @@ -74,12 +75,15 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig if isServer { template.NotAfter = tenYearsFromNow template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageServerAuth) - if ip := net.ParseIP(host); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, host) + for _, host := range hosts { + if ip := net.ParseIP(host); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, host) + } } } + template.SubjectKeyId = computeSKI(&privateKey.PublicKey) // If no parent cert, it's a self signed cert if parent == nil || certSigner == nil { parent = &template @@ -111,3 +115,10 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig func encodePEM(keyType string, data []byte) []byte { return pem.EncodeToMemory(&pem.Block{Type: keyType, Bytes: data}) } + +// RFC 7093, Section 2, Method 4 +func computeSKI(key *ecdsa.PublicKey) []byte { + raw := elliptic.Marshal(key.Curve, key.X, key.Y) + hash := sha256.Sum256(raw) + return hash[:] +} diff --git a/common/crypto/tlsgen/key_test.go b/common/crypto/tlsgen/key_test.go index 64bda627c48..aff2123108a 100644 --- a/common/crypto/tlsgen/key_test.go +++ b/common/crypto/tlsgen/key_test.go @@ -16,7 +16,7 @@ import ( ) func TestLoadCert(t *testing.T) { - pair, err := newCertKeyPair(false, false, "", nil, nil) + pair, err := newCertKeyPair(false, false, nil, nil) assert.NoError(t, err) assert.NotNil(t, pair) tlsCertPair, err := tls.X509KeyPair(pair.Cert, pair.Key) diff --git a/internal/cryptogen/ca/ca.go b/internal/cryptogen/ca/ca.go index 95ea31e0ffc..f9cacddb81a 100644 --- a/internal/cryptogen/ca/ca.go +++ b/internal/cryptogen/ca/ca.go @@ -167,7 +167,7 @@ func (ca *CA) SignCertificate( return cert, nil } -// compute Subject Key Identifier +// compute Subject Key Identifier using RFC 7093, Section 2, Method 4 func computeSKI(privKey *ecdsa.PrivateKey) []byte { // Marshall the public key raw := elliptic.Marshal(privKey.Curve, privKey.PublicKey.X, privKey.PublicKey.Y)