Skip to content

Commit

Permalink
Add support for PKCS8 private keys in CA certs
Browse files Browse the repository at this point in the history
  • Loading branch information
maelk committed Jun 12, 2020
1 parent c97bdd7 commit 83c1245
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
15 changes: 12 additions & 3 deletions controlplane/kubeadm/internal/workload_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ func (w *Workload) ClusterStatus(ctx context.Context) (ClusterStatus, error) {
}

func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, error) {
var caKey interface{}
privKey, err := certs.NewPrivateKey()
if err != nil {
return tls.Certificate{}, err
Expand All @@ -300,18 +301,26 @@ func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, er
if err != nil {
return tls.Certificate{}, err
}
caKey, err := certs.DecodePrivateKeyPEM(caKeyEncoded)
caKey, err = certs.DecodePrivateKeyPEM(caKeyEncoded)
if err != nil {
return tls.Certificate{}, err
pkcs8CAKey, pkcs8Err := certs.DecodePKCS8PrivateKeyPEM(caKeyEncoded)
if pkcs8Err != nil {
return tls.Certificate{}, err
}
caKey = pkcs8CAKey
}
x509Cert, err := newClientCert(caCert, privKey, caKey)
x509Cert, err := newPKCS8ClientCert(caCert, privKey, caKey)
if err != nil {
return tls.Certificate{}, err
}
return tls.X509KeyPair(certs.EncodeCertPEM(x509Cert), certs.EncodePrivateKeyPEM(privKey))
}

func newClientCert(caCert *x509.Certificate, key *rsa.PrivateKey, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
return newPKCS8ClientCert(caCert, key, caKey)
}

func newPKCS8ClientCert(caCert *x509.Certificate, key *rsa.PrivateKey, caKey interface{}) (*x509.Certificate, error) {
cfg := certs.Config{
CommonName: "cluster-api.x-k8s.io",
}
Expand Down
14 changes: 14 additions & 0 deletions util/certs/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,17 @@ func DecodePrivateKeyPEM(encoded []byte) (*rsa.PrivateKey, error) {

return x509.ParsePKCS1PrivateKey(block.Bytes)
}

// DecodePKCS8PrivateKeyPEM attempts to return a decoded key or nil
// if the encoded input does not contain a private key.
func DecodePKCS8PrivateKeyPEM(encoded []byte) (interface{}, error) {
block, _ := pem.Decode(encoded)
if block == nil {
return nil, nil
}
// ParsePKCS1PrivateKey will fail with errors.New for many reasons
// including if the format is wrong, so we can retry with PKCS8
// https://golang.org/src/crypto/x509/pkcs1.go#L58
return x509.ParsePKCS8PrivateKey(block.Bytes)

}
5 changes: 5 additions & 0 deletions util/certs/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type Config struct {

// NewSignedCert creates a signed certificate using the given CA certificate and key.
func (cfg *Config) NewSignedCert(key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
return cfg.NewSignedCertPKCS8(key, caCert, caKey)
}

// NewSignedCertPKCS8 creates a signed certificate using the given CA certificate and PKCS8 format key.
func (cfg *Config) NewSignedCertPKCS8(key *rsa.PrivateKey, caCert *x509.Certificate, caKey interface{}) (*x509.Certificate, error) {
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, errors.Wrap(err, "failed to generate random integer for signed cerficate")
Expand Down
22 changes: 17 additions & 5 deletions util/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func FromSecret(ctx context.Context, c client.Reader, cluster client.ObjectKey)

// New creates a new Kubeconfig using the cluster name and specified endpoint.
func New(clusterName, endpoint string, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*api.Config, error) {
return NewPKCS8(clusterName, endpoint, caCert, caKey)
}

// NewPKCS8 creates a new Kubeconfig using the cluster name and specified endpoint with PKCS8 format key.
func NewPKCS8(clusterName, endpoint string, caCert *x509.Certificate, caKey interface{}) (*api.Config, error) {
cfg := &certs.Config{
CommonName: "kubernetes-admin",
Organization: []string{"system:masters"},
Expand All @@ -62,7 +67,7 @@ func New(clusterName, endpoint string, caCert *x509.Certificate, caKey *rsa.Priv
return nil, errors.Wrap(err, "unable to create private key")
}

clientCert, err := cfg.NewSignedCert(clientKey, caCert, caKey)
clientCert, err := cfg.NewSignedCertPKCS8(clientKey, caCert, caKey)
if err != nil {
return nil, errors.Wrap(err, "unable to sign certificate")
}
Expand Down Expand Up @@ -213,14 +218,21 @@ func generateKubeconfig(ctx context.Context, c client.Client, clusterName client
return nil, errors.New("certificate not found in config")
}

key, err := certs.DecodePrivateKeyPEM(clusterCA.Data[secret.TLSKeyDataName])
var key interface{}
key, err = certs.DecodePrivateKeyPEM(clusterCA.Data[secret.TLSKeyDataName])
if err != nil {
return nil, errors.Wrap(err, "failed to decode private key")
} else if key == nil {
pkcs8CAKey, pkcs8Err := certs.DecodePKCS8PrivateKeyPEM(clusterCA.Data[secret.TLSKeyDataName])
if pkcs8Err != nil {
return nil, errors.Wrap(err, "failed to decode private key")
}
key = pkcs8CAKey
}

if key == nil {
return nil, errors.New("CA private key not found")
}

cfg, err := New(clusterName.Name, endpoint, cert, key)
cfg, err := NewPKCS8(clusterName.Name, endpoint, cert, key)
if err != nil {
return nil, errors.Wrap(err, "failed to generate a kubeconfig")
}
Expand Down

0 comments on commit 83c1245

Please sign in to comment.