diff --git a/testing/certutil/certutil.go b/testing/certutil/certutil.go index 075460ed..5c8d6956 100644 --- a/testing/certutil/certutil.go +++ b/testing/certutil/certutil.go @@ -51,16 +51,16 @@ func NewRootCA() (*ecdsa.PrivateKey, *x509.Certificate, Pair, error) { return nil, nil, Pair{}, fmt.Errorf("could not create private key: %w", err) } - notBefore := time.Now() - notAfter := notBefore.Add(3 * time.Hour) + notBefore, notAfter := makeNotBeforeAndAfter() rootTemplate := x509.Certificate{ - DNSNames: []string{"localhost"}, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, SerialNumber: big.NewInt(1653), Subject: pkix.Name{ - Organization: []string{"Gallifrey"}, - CommonName: "localhost", + Country: []string{"Gallifrey"}, + Locality: []string{"The Capitol"}, + OrganizationalUnit: []string{"Time Lords"}, + Organization: []string{"High Council of the Time Lords"}, + CommonName: "High Council", }, NotBefore: notBefore, NotAfter: notAfter, @@ -125,16 +125,16 @@ func NewRootCA() (*ecdsa.PrivateKey, *x509.Certificate, Pair, error) { // If any error occurs during the generation process, a non-nil error is returned. func GenerateChildCert(name string, ips []net.IP, caPrivKey crypto.PrivateKey, caCert *x509.Certificate) (*tls.Certificate, Pair, error) { - notBefore := time.Now() - notAfter := notBefore.Add(3 * time.Hour) + notBefore, notAfter := makeNotBeforeAndAfter() certTemplate := &x509.Certificate{ DNSNames: []string{name}, IPAddresses: ips, SerialNumber: big.NewInt(1658), Subject: pkix.Name{ - Organization: []string{"Gallifrey"}, - CommonName: name, + Locality: []string{"anywhere in time and space"}, + Organization: []string{"TARDIS"}, + CommonName: "Police Public Call Box", }, NotBefore: notBefore, NotAfter: notAfter, @@ -212,3 +212,10 @@ func NewRootAndChildCerts() (Pair, Pair, error) { return rootPair, childPair, nil } + +func makeNotBeforeAndAfter() (time.Time, time.Time) { + now := time.Now() + notBefore := now.Add(-1 * time.Minute) + notAfter := now.Add(7 * 24 * time.Hour) + return notBefore, notAfter +} diff --git a/testing/certutil/cmd/main.go b/testing/certutil/cmd/main.go index d833d689..fb0ae64d 100644 --- a/testing/certutil/cmd/main.go +++ b/testing/certutil/cmd/main.go @@ -19,20 +19,23 @@ package main import ( "crypto" + "crypto/rand" "crypto/tls" "crypto/x509" + "encoding/pem" "flag" "fmt" "net" "os" "path/filepath" "strings" + "time" "github.com/elastic/elastic-agent-libs/testing/certutil" ) func main() { - var caPath, caKeyPath, dest, name, ipList string + var caPath, caKeyPath, dest, name, ipList, filePrefix, pass string flag.StringVar(&caPath, "ca", "", "File path for CA in PEM format") flag.StringVar(&caKeyPath, "ca-key", "", @@ -43,6 +46,10 @@ func main() { "used as \"distinguished name\" and \"Subject Alternate Name values\" for the child certificate") flag.StringVar(&ipList, "ips", "127.0.0.1", "a comma separated list of IP addresses for the child certificate") + flag.StringVar(&filePrefix, "prefix", "current timestamp", + "a prefix to be added to the file name. If not provided a timestamp will be used") + flag.StringVar(&pass, "pass", "", + "a passphrase to encrypt the certificate key") flag.Parse() if caPath == "" && caKeyPath != "" || caPath != "" && caKeyPath == "" { @@ -52,6 +59,16 @@ func main() { caPath, caKeyPath) } + if filePrefix == "" { + filePrefix = fmt.Sprintf("%d", time.Now().Unix()) + } + filePrefix += "-" + + wd, err := os.Getwd() + if err != nil { + fmt.Printf("error getting current working directory: %v\n", err) + } + fmt.Println("files will be witten to:", wd) ips := strings.Split(ipList, ",") var netIPs []net.IP @@ -61,7 +78,6 @@ func main() { var rootCert *x509.Certificate var rootKey crypto.PrivateKey - var err error if caPath == "" && caKeyPath == "" { var pair certutil.Pair rootKey, rootCert, pair, err = certutil.NewRootCA() @@ -69,17 +85,50 @@ func main() { panic(fmt.Errorf("could not create root CA certificate: %w", err)) } - savePair(dest, "ca", pair) + savePair(dest, filePrefix+"ca", pair) } else { rootKey, rootCert = loadCA(caPath, caKeyPath) } - _, childPair, err := certutil.GenerateChildCert(name, netIPs, rootKey, rootCert) + childCert, childPair, err := certutil.GenerateChildCert(name, netIPs, rootKey, rootCert) if err != nil { panic(fmt.Errorf("error generating child certificate: %w", err)) } - savePair(dest, name, childPair) + savePair(dest, filePrefix+name, childPair) + + if pass == "" { + return + } + + fmt.Printf("passphrase present, encrypting \"%s\" certificate key\n", + name) + err = os.WriteFile(filePrefix+name+"-passphrase", []byte(pass), 0o600) + if err != nil { + panic(fmt.Errorf("error writing passphrase file: %w", err)) + } + + key, err := x509.MarshalPKCS8PrivateKey(childCert.PrivateKey) + if err != nil { + panic(fmt.Errorf("error getting ecdh.PrivateKey from the child's private key: %w", err)) + } + + encPem, err := x509.EncryptPEMBlock( //nolint:staticcheck // we need to drop support for this, but while we don't, it needs to be tested. + rand.Reader, + "EC PRIVATE KEY", + key, + []byte(pass), + x509.PEMCipherAES128) + if err != nil { + panic(fmt.Errorf("failed encrypting agent child certificate key block: %v", err)) + } + + certKeyEnc := pem.EncodeToMemory(encPem) + + err = os.WriteFile(filepath.Join(dest, filePrefix+name+"_enc-key.pem"), certKeyEnc, 0o600) + if err != nil { + panic(fmt.Errorf("could not save %s certificate encrypted key: %w", filePrefix+name+"_enc-key.pem", err)) + } } func loadCA(caPath string, keyPath string) (crypto.PrivateKey, *x509.Certificate) {