Skip to content

Commit

Permalink
Set SKI, support multi hosts, add Signer to CA
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
  • Loading branch information
sykesm authored and Jason Yellick committed Feb 15, 2021
1 parent 684e255 commit aa7ad4f
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 13 deletions.
20 changes: 14 additions & 6 deletions common/crypto/tlsgen/ca.go
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
6 changes: 6 additions & 0 deletions common/crypto/tlsgen/ca_test.go
Expand Up @@ -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())
}
21 changes: 16 additions & 5 deletions common/crypto/tlsgen/key.go
Expand Up @@ -11,6 +11,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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[:]
}
2 changes: 1 addition & 1 deletion common/crypto/tlsgen/key_test.go
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion internal/cryptogen/ca/ca.go
Expand Up @@ -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)
Expand Down

0 comments on commit aa7ad4f

Please sign in to comment.