From d625e83a88c76fbdc2d5b4b69c04161ee325e880 Mon Sep 17 00:00:00 2001 From: Martin Weindel Date: Tue, 24 Oct 2023 12:56:17 +0200 Subject: [PATCH] supporting PKCS8 --- hack/check-cert-secret.sh | 6 +- pkg/cert/legobridge/cakeypair_test.go | 85 ++++++++++++++++++++ pkg/cert/legobridge/legobridge_suite_test.go | 19 +++++ pkg/cert/legobridge/pki.go | 13 +-- 4 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 pkg/cert/legobridge/cakeypair_test.go create mode 100644 pkg/cert/legobridge/legobridge_suite_test.go diff --git a/hack/check-cert-secret.sh b/hack/check-cert-secret.sh index 366bfe6a..9cfd2f45 100755 --- a/hack/check-cert-secret.sh +++ b/hack/check-cert-secret.sh @@ -52,10 +52,10 @@ prepareParts() # extract certificate from certificate secret kubectl -n $NS get secret $(kubectl -n $NS get certificates.cert.gardener.cloud $CERTNAME -o=jsonpath='{.spec.secretRef.name}') -o=jsonpath='{.data.tls\.crt}' | base64 -d > "$X509CERT" + CSPLIT=gcsplit # use gnu csplit on mac (install with "brew install coreutils") + which $CSPLIT || export CSPLIT=csplit # split certificate and chain - - #cat $X509CERT | awk -v PREFIX="$PREFIX" 'split_after == 1 {n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > (PREFIX "cert" n ".pem")}' - csplit -s -z -f "$PREFIX/cert-" -b "%d.pem" $X509CERT '/-----BEGIN CERTIFICATE-----/' '{*}' + $CSPLIT -s -z -f "$PREFIX/cert-" -b "%d.pem" $X509CERT '/-----BEGIN CERTIFICATE-----/' '{*}' } cleanup() diff --git a/pkg/cert/legobridge/cakeypair_test.go b/pkg/cert/legobridge/cakeypair_test.go new file mode 100644 index 00000000..b965b3cf --- /dev/null +++ b/pkg/cert/legobridge/cakeypair_test.go @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package legobridge + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" +) + +var _ = Describe("PKI Helpers", func() { + Context("CAKeyPairFromSecretData", func() { + It("works for ec key", func() { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + Expect(err).To(Succeed()) + keyBytes, err := x509.MarshalECPrivateKey(priv) + Expect(err).To(Succeed()) + data, err := createCertificate(priv, priv.Public(), "EC PRIVATE KEY", keyBytes) + Expect(err).To(Succeed()) + + _, err = CAKeyPairFromSecretData(data) + Expect(err).To(Succeed()) + }) + + It("works for PKCS1 rsa key", func() { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + Expect(err).To(Succeed()) + keyBytes := x509.MarshalPKCS1PrivateKey(priv) + data, err := createCertificate(priv, priv.Public(), "RSA PRIVATE KEY", keyBytes) + Expect(err).To(Succeed()) + + _, err = CAKeyPairFromSecretData(data) + Expect(err).To(Succeed()) + }) + + It("works for PKCS8 rsa key", func() { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + Expect(err).To(Succeed()) + keyBytes, err := x509.MarshalPKCS8PrivateKey(priv) + Expect(err).To(Succeed()) + data, err := createCertificate(priv, priv.Public(), "PRIVATE KEY", keyBytes) + Expect(err).To(Succeed()) + + _, err = CAKeyPairFromSecretData(data) + Expect(err).To(Succeed()) + }) + }) +}) + +func createCertificate(privKey crypto.PrivateKey, pubKey crypto.PublicKey, header string, privKeyBytes []byte) (map[string][]byte, error) { + template := &x509.Certificate{ + SerialNumber: big.NewInt(1234), + Subject: pkix.Name{CommonName: "example.com"}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, privKey) + if err != nil { + return nil, err + } + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: header, Bytes: privKeyBytes}) + return map[string][]byte{ + corev1.TLSCertKey: certPEM, + corev1.TLSPrivateKeyKey: keyPEM, + }, nil +} diff --git a/pkg/cert/legobridge/legobridge_suite_test.go b/pkg/cert/legobridge/legobridge_suite_test.go new file mode 100644 index 00000000..1cfe7228 --- /dev/null +++ b/pkg/cert/legobridge/legobridge_suite_test.go @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package legobridge_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCore(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Legobridge Suite") +} diff --git a/pkg/cert/legobridge/pki.go b/pkg/cert/legobridge/pki.go index 5a2d7033..516f0e21 100644 --- a/pkg/cert/legobridge/pki.go +++ b/pkg/cert/legobridge/pki.go @@ -33,8 +33,6 @@ var ( ) const ( - // DefaultSigAlgo is the default Signature Algorithm (letsencrypt default). - DefaultSigAlgo x509.SignatureAlgorithm = x509.SHA256WithRSA // DefaultPubKeyAlgo is the default Public Key Algorithm (letsencrypt default). DefaultPubKeyAlgo x509.PublicKeyAlgorithm = x509.RSA // DefaultCertKeyUsage is the default Key Usage (letsencrypt default). @@ -389,10 +387,13 @@ func bytesToPrivateKey(data []byte) (crypto.PrivateKey, error) { if err == nil { return key, nil } - key2, err2 := x509.ParsePKCS1PrivateKey(block.Bytes) - if err2 != nil { - return nil, fmt.Errorf("decoding private key failed with %s (ec) and %s (rsa)", err, err2) + if err2 == nil { + return key2, nil + } + key3, err3 := x509.ParsePKCS8PrivateKey(block.Bytes) + if err3 != nil { + return nil, fmt.Errorf("decoding private key failed with %s (ec) and %s (rsa PKCS1) and %s (PKCS8)", err, err2, err3) } - return key2, nil + return key3, nil }