internal/pkg/testing/certs/certs.go (153 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. package certs import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" "net" "os" "path/filepath" "testing" "time" ) const ext = ".pem" func CertToFile(t *testing.T, cert tls.Certificate, name string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, name+ext) file, err := os.Create(path) if err != nil { t.Fatalf("unable to create file: %v", err) } if err := pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Certificate[0]}); err != nil { t.Fatalf("unable to write cert: %v", err) } if err := file.Close(); err != nil { t.Fatalf("unable to close file: %v", err) } return path } func KeyToFile(t *testing.T, cert tls.Certificate, name string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, name+ext) file, err := os.Create(path) if err != nil { t.Fatalf("unable to create file: %v", err) } p, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey) if err != nil { t.Fatalf("unable to marshal private key: %v", err) } if err := pem.Encode(file, &pem.Block{Type: "PRIVATE KEY", Bytes: p}); err != nil { t.Fatalf("unable to write key: %v", err) } if err := file.Close(); err != nil { t.Fatalf("unable to close file: %v", err) } return path } // GenCA generates a CA for tests // copied from elastic-agent-libs/transport/tlscommon/ca_pinning_test.go func GenCA(t *testing.T) tls.Certificate { t.Helper() ca := &x509.Certificate{ SerialNumber: big.NewInt(2000), Subject: pkix.Name{ CommonName: "localhost", Organization: []string{"TESTING"}, Country: []string{"CANADA"}, Province: []string{"QUEBEC"}, Locality: []string{"MONTREAL"}, StreetAddress: []string{"testing road"}, PostalCode: []string{"HOH OHO"}, }, NotBefore: time.Now(), NotAfter: time.Now().Add(1 * time.Hour), IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, } caKey, err := rsa.GenerateKey(rand.Reader, 2048) // less secure key for quicker testing. if err != nil { t.Fatalf("fail to generate RSA key: %v", err) } ca.SubjectKeyId = generateSubjectKeyID(caKey) caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey) if err != nil { t.Fatalf("fail to create certificate: %v", err) } leaf, err := x509.ParseCertificate(caBytes) if err != nil { t.Fatalf("fail to parse certificate: %v", err) } return tls.Certificate{ Certificate: [][]byte{caBytes}, PrivateKey: caKey, Leaf: leaf, } } func generateSubjectKeyID(pub crypto.PublicKey) []byte { // SubjectKeyId generated using method 1 in RFC 7093, Section 2: // 1) The keyIdentifier is composed of the leftmost 160-bits of the // SHA-256 hash of the value of the BIT STRING subjectPublicKey // (excluding the tag, length, and number of unused bits). var publicKeyBytes []byte switch publicKey := pub.(type) { case *rsa.PublicKey: publicKeyBytes = x509.MarshalPKCS1PublicKey(publicKey) case *ecdsa.PublicKey: //nolint:staticcheck // no alternative publicKeyBytes = elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y) } h := sha256.Sum256(publicKeyBytes) return h[:20] } // GenCert generates a test keypair and signs the cert with the passed CA. // copied from elastic-agent-libs/transport/tlscommon/ca_pinning_test.go func GenCert(t *testing.T, ca tls.Certificate) tls.Certificate { t.Helper() ts := time.Now().UTC() cert := &x509.Certificate{ SerialNumber: big.NewInt(2000), // Subject Alternative Name fields IPAddresses: []net.IP{{127, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, DNSNames: []string{"localhost"}, Subject: pkix.Name{ CommonName: "fleet-server testing", Organization: []string{"TESTING"}, Country: []string{"CANADA"}, Province: []string{"QUEBEC"}, Locality: []string{"MONTREAL"}, StreetAddress: []string{"testing road"}, PostalCode: []string{"HOH OHO"}, }, NotBefore: ts, NotAfter: ts.Add(24 * time.Hour), IsCA: false, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, BasicConstraintsValid: true, } certKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("fail to generate RSA key: %v", err) } certBytes, err := x509.CreateCertificate( rand.Reader, cert, ca.Leaf, &certKey.PublicKey, ca.PrivateKey, ) if err != nil { t.Fatalf("fail to create signed certificate: %v", err) } leaf, err := x509.ParseCertificate(certBytes) if err != nil { t.Fatalf("fail to parse the certificate: %v", err) } return tls.Certificate{ Certificate: [][]byte{certBytes}, PrivateKey: certKey, Leaf: leaf, } }