Merge pull request #76390 from rojkov/ecdsa-v2

kubeadm: add support for ECDSA keys
This commit is contained in:
Kubernetes Prow Robot
2019-04-26 19:56:23 -07:00
committed by GitHub
14 changed files with 148 additions and 98 deletions

View File

@@ -17,6 +17,7 @@ limitations under the License.
package certs
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"net"
@@ -30,7 +31,7 @@ import (
// SetupCertificateAuthorithy is a utility function for kubeadm testing that creates a
// CertificateAuthorithy cert/key pair
func SetupCertificateAuthorithy(t *testing.T) (*x509.Certificate, *rsa.PrivateKey) {
func SetupCertificateAuthorithy(t *testing.T) (*x509.Certificate, crypto.Signer) {
caCert, caKey, err := pkiutil.NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
if err != nil {
t.Fatalf("failure while generating CA certificate and key: %v", err)
@@ -130,7 +131,7 @@ func AssertCertificateHasIPAddresses(t *testing.T, cert *x509.Certificate, IPAdd
}
// CreateCACert creates a generic CA cert.
func CreateCACert(t *testing.T) (*x509.Certificate, *rsa.PrivateKey) {
func CreateCACert(t *testing.T) (*x509.Certificate, crypto.Signer) {
certCfg := &certutil.Config{CommonName: "kubernetes"}
cert, key, err := pkiutil.NewCertificateAuthority(certCfg)
if err != nil {
@@ -140,7 +141,7 @@ func CreateCACert(t *testing.T) (*x509.Certificate, *rsa.PrivateKey) {
}
// CreateTestCert makes a generic certificate with the given CA and alternative names.
func CreateTestCert(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*x509.Certificate, *rsa.PrivateKey, *certutil.Config) {
func CreateTestCert(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, altNames certutil.AltNames) (*x509.Certificate, crypto.Signer, *certutil.Config) {
config := &certutil.Config{
CommonName: "testCert",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},

View File

@@ -18,6 +18,7 @@ package pkiutil
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
cryptorand "crypto/rand"
"crypto/rsa"
@@ -58,7 +59,7 @@ const (
)
// NewCertificateAuthority creates new certificate and private key for the certificate authority
func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, *rsa.PrivateKey, error) {
func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey()
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key while generating CA certificate")
@@ -73,7 +74,7 @@ func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, *rsa.P
}
// NewCertAndKey creates new certificate and key by passing the certificate authority certificate and key
func NewCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey, config *certutil.Config) (*x509.Certificate, *rsa.PrivateKey, error) {
func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey()
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key")
@@ -88,7 +89,7 @@ func NewCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey, config *cert
}
// NewCSRAndKey generates a new key and CSR and that could be signed to create the given certificate
func NewCSRAndKey(config *certutil.Config) (*x509.CertificateRequest, *rsa.PrivateKey, error) {
func NewCSRAndKey(config *certutil.Config) (*x509.CertificateRequest, crypto.Signer, error) {
key, err := NewPrivateKey()
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key")
@@ -113,7 +114,7 @@ func HasServerAuth(cert *x509.Certificate) bool {
}
// WriteCertAndKey stores certificate and key at the specified location
func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key *rsa.PrivateKey) error {
func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key crypto.Signer) error {
if err := WriteKey(pkiPath, name, key); err != nil {
return errors.Wrap(err, "couldn't write key")
}
@@ -136,7 +137,7 @@ func WriteCert(pkiPath, name string, cert *x509.Certificate) error {
}
// WriteKey stores the given key at the given location
func WriteKey(pkiPath, name string, key *rsa.PrivateKey) error {
func WriteKey(pkiPath, name string, key crypto.Signer) error {
if key == nil {
return errors.New("private key cannot be nil when writing to file")
}
@@ -175,7 +176,7 @@ func WriteCSR(csrDir, name string, csr *x509.CertificateRequest) error {
}
// WritePublicKey stores the given public key at the given location
func WritePublicKey(pkiPath, name string, key *rsa.PublicKey) error {
func WritePublicKey(pkiPath, name string, key crypto.PublicKey) error {
if key == nil {
return errors.New("public key cannot be nil when writing to file")
}
@@ -219,7 +220,7 @@ func CSROrKeyExist(csrDir, name string) bool {
}
// TryLoadCertAndKeyFromDisk tries to load a cert and a key from the disk and validates that they are valid
func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, *rsa.PrivateKey, error) {
func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, crypto.Signer, error) {
cert, err := TryLoadCertFromDisk(pkiPath, name)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to load certificate")
@@ -259,7 +260,7 @@ func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
}
// TryLoadKeyFromDisk tries to load the key from the disk and validates that it is valid
func TryLoadKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, error) {
func TryLoadKeyFromDisk(pkiPath, name string) (crypto.Signer, error) {
privateKeyPath := pathForKey(pkiPath, name)
// Parse the private key from a file
@@ -268,20 +269,22 @@ func TryLoadKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, error) {
return nil, errors.Wrapf(err, "couldn't load the private key file %s", privateKeyPath)
}
// Allow RSA format only
var key *rsa.PrivateKey
// Allow RSA and ECDSA formats only
var key crypto.Signer
switch k := privKey.(type) {
case *rsa.PrivateKey:
key = k
case *ecdsa.PrivateKey:
key = k
default:
return nil, errors.Errorf("the private key file %s isn't in RSA format", privateKeyPath)
return nil, errors.Errorf("the private key file %s is neither in RSA nor ECDSA format", privateKeyPath)
}
return key, nil
}
// TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, *rsa.PrivateKey, error) {
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, crypto.Signer, error) {
csrPath := pathForCSR(pkiPath, name)
csr, err := CertificateRequestFromFile(csrPath)
@@ -528,7 +531,7 @@ func EncodeCertPEM(cert *x509.Certificate) []byte {
}
// EncodePublicKeyPEM returns PEM-encoded public data
func EncodePublicKeyPEM(key *rsa.PublicKey) ([]byte, error) {
func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) {
der, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return []byte{}, err
@@ -541,7 +544,7 @@ func EncodePublicKeyPEM(key *rsa.PublicKey) ([]byte, error) {
}
// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
func NewPrivateKey() (crypto.Signer, error) {
return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}

View File

@@ -17,6 +17,9 @@ limitations under the License.
package pkiutil
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -49,27 +52,38 @@ func TestNewCertificateAuthority(t *testing.T) {
func TestNewCertAndKey(t *testing.T) {
var tests = []struct {
name string
caKeySize int
expected bool
name string
keyGenFunc func() (crypto.Signer, error)
expected bool
}{
{
name: "RSA key too small",
caKeySize: 128,
expected: false,
name: "RSA key too small",
keyGenFunc: func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 128)
},
expected: false,
},
{
name: "Should succeed",
caKeySize: 2048,
expected: true,
name: "RSA should succeed",
keyGenFunc: func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 2048)
},
expected: true,
},
{
name: "ECDSA should succeed",
keyGenFunc: func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
},
expected: true,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize)
caKey, err := rt.keyGenFunc()
if err != nil {
t.Fatalf("Couldn't create rsa Private Key")
t.Fatalf("Couldn't create Private Key")
}
caCert := &x509.Certificate{}
config := &certutil.Config{
@@ -375,49 +389,66 @@ func TestTryLoadCertFromDisk(t *testing.T) {
}
func TestTryLoadKeyFromDisk(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
_, caKey, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
if err != nil {
t.Errorf(
"failed to create cert and key with an error: %v",
err,
)
}
err = WriteKey(tmpdir, "foo", caKey)
if err != nil {
t.Errorf(
"failed to write cert and key with an error: %v",
err,
)
}
var tests = []struct {
desc string
path string
name string
expected bool
desc string
pathSuffix string
name string
keyGenFunc func() (crypto.Signer, error)
expected bool
}{
{
desc: "empty path and name",
path: "",
name: "",
desc: "empty path and name",
pathSuffix: "somegarbage",
name: "",
keyGenFunc: func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 2048)
},
expected: false,
},
{
desc: "valid path and name",
path: tmpdir,
name: "foo",
desc: "RSA valid path and name",
pathSuffix: "",
name: "foo",
keyGenFunc: func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 2048)
},
expected: true,
},
{
desc: "ECDSA valid path and name",
pathSuffix: "",
name: "foo",
keyGenFunc: func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
},
expected: true,
},
}
for _, rt := range tests {
t.Run(rt.desc, func(t *testing.T) {
_, actual := TryLoadKeyFromDisk(rt.path, rt.name)
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
caKey, err := rt.keyGenFunc()
if err != nil {
t.Errorf(
"failed to create key with an error: %v",
err,
)
}
err = WriteKey(tmpdir, "foo", caKey)
if err != nil {
t.Errorf(
"failed to write key with an error: %v",
err,
)
}
_, actual := TryLoadKeyFromDisk(tmpdir+rt.pathSuffix, rt.name)
if (actual == nil) != rt.expected {
t.Errorf(
"failed TryLoadCertAndKeyFromDisk:\n\texpected: %t\n\t actual: %t",