From f29e2195d38355f16ae13e9af138e6c70b78fbfa Mon Sep 17 00:00:00 2001 From: Mario Valderrama <15158349+avorima@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:36:00 +0100 Subject: [PATCH] feat: support ECDSA private keys for etcd (#667) * feat: support ECDSA private keys Signed-off-by: Mario Valderrama * fix: use jetstack cert-manager chart Signed-off-by: Mario Valderrama --------- Signed-off-by: Mario Valderrama --- Makefile | 4 ++-- internal/crypto/crypto.go | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c1c58f4..12ab41f 100644 --- a/Makefile +++ b/Makefile @@ -229,8 +229,8 @@ metallb: cat hack/metallb.yaml | sed -E "s|172.19|$$(docker network inspect -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' kind | sed -E 's|^([0-9]+\.[0-9]+)\..*$$|\1|g')|g" | kubectl apply -f - cert-manager: - $(HELM) repo add bitnami https://charts.bitnami.com/bitnami - $(HELM) upgrade --install cert-manager bitnami/cert-manager --namespace certmanager-system --create-namespace --set "installCRDs=true" + $(HELM) repo add jetstack https://charts.jetstack.io + $(HELM) upgrade --install cert-manager jetstack/cert-manager --namespace certmanager-system --create-namespace --set "installCRDs=true" load: kind $(KIND) load docker-image --name kamaji ${CONTAINER_REPOSITORY}:${VERSION} diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index 1ea5c01..14f2b18 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -5,6 +5,7 @@ package crypto import ( "bytes" + "crypto" cryptorand "crypto/rand" "crypto/rsa" "crypto/x509" @@ -36,7 +37,7 @@ func CheckPublicAndPrivateKeyValidity(publicKey []byte, privateKey []byte) (bool return false, err } - return checkPublicKeys(privKey.PublicKey, *pubKey), nil + return checkPublicKeys(pubKey, privKey), nil } // CheckCertificateSAN checks if the Kubernetes API Server certificate matches the SAN stored in the kubeadm: @@ -114,12 +115,21 @@ func ParseCertificateBytes(content []byte) (*x509.Certificate, error) { } // ParsePrivateKeyBytes takes the private key bytes returning an RSA private key by parsing it. -func ParsePrivateKeyBytes(content []byte) (*rsa.PrivateKey, error) { +func ParsePrivateKeyBytes(content []byte) (crypto.Signer, error) { pemContent, _ := pem.Decode(content) if pemContent == nil { return nil, fmt.Errorf("no right PEM block") } + if pemContent.Type == "EC PRIVATE KEY" { + privateKey, err := x509.ParseECPrivateKey(pemContent.Bytes) + if err != nil { + return nil, errors.Wrap(err, "cannot parse EC Private Key") + } + + return privateKey, nil + } + privateKey, err := x509.ParsePKCS1PrivateKey(pemContent.Bytes) if err != nil { return nil, errors.Wrap(err, "cannot parse PKCS1 Private Key") @@ -163,7 +173,7 @@ func IsValidCertificateKeyPairBytes(certificateBytes []byte, privateKeyBytes []b switch { case !checkCertificateValidity(*crt): return false, nil - case !checkPublicKeys(*crt.PublicKey.(*rsa.PublicKey), key.PublicKey): //nolint:forcetypeassert + case !checkPublicKeys(crt.PublicKey, key): return false, nil default: return true, nil @@ -196,7 +206,7 @@ func VerifyCertificate(cert, ca []byte, usages ...x509.ExtKeyUsage) (bool, error return len(chains) > 0, err } -func generateCertificateKeyPairBytes(template *x509.Certificate, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*bytes.Buffer, *bytes.Buffer, error) { +func generateCertificateKeyPairBytes(template *x509.Certificate, caCert *x509.Certificate, caKey crypto.Signer) (*bytes.Buffer, *bytes.Buffer, error) { certPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 2048) if err != nil { return nil, nil, errors.Wrap(err, "cannot generate an RSA key") @@ -236,11 +246,12 @@ func checkCertificateValidity(cert x509.Certificate) bool { return notAfter && notBefore } -func checkPublicKeys(a rsa.PublicKey, b rsa.PublicKey) bool { - isN := a.N.Cmp(b.N) == 0 - isE := a.E == b.E +func checkPublicKeys(a crypto.PublicKey, b crypto.Signer) bool { + if key, ok := a.(interface{ Equal(k crypto.PublicKey) bool }); ok { + return key.Equal(b.Public()) + } - return isN && isE + return false } // NewCertificateTemplate returns the template that must be used to generate a certificate,