Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testing/certutil/cmd: add passphrase protected key support #230

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions testing/certutil/certutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
59 changes: 54 additions & 5 deletions testing/certutil/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@

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", "",
Expand All @@ -43,6 +46,10 @@
"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 == "" {
Expand All @@ -52,6 +59,16 @@
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)

Check failure on line 69 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (windows)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 69 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (linux)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 69 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (darwin)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)
}
fmt.Println("files will be witten to:", wd)

Check failure on line 71 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (windows)

use of `fmt.Println` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 71 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (linux)

use of `fmt.Println` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 71 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (darwin)

use of `fmt.Println` forbidden by pattern `fmt.Print.*` (forbidigo)

ips := strings.Split(ipList, ",")
var netIPs []net.IP
Expand All @@ -61,25 +78,57 @@

var rootCert *x509.Certificate
var rootKey crypto.PrivateKey
var err error
if caPath == "" && caKeyPath == "" {
var pair certutil.Pair
rootKey, rootCert, pair, err = certutil.NewRootCA()
if err != nil {
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",

Check failure on line 104 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (windows)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 104 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (linux)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)

Check failure on line 104 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (darwin)

use of `fmt.Printf` forbidden by pattern `fmt.Print.*` (forbidigo)
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))

Check failure on line 123 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (windows)

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)

Check failure on line 123 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (linux)

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)

Check failure on line 123 in testing/certutil/cmd/main.go

View workflow job for this annotation

GitHub Actions / lint (darwin)

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

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) {
Expand Down
Loading