Skip to content

Commit 6c381fb

Browse files
authored
testing/certutil/cmd: add passphrase protected key support (#230)
1 parent 4babd25 commit 6c381fb

File tree

2 files changed

+71
-15
lines changed

2 files changed

+71
-15
lines changed

testing/certutil/certutil.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,16 @@ func NewRootCA() (*ecdsa.PrivateKey, *x509.Certificate, Pair, error) {
5151
return nil, nil, Pair{}, fmt.Errorf("could not create private key: %w", err)
5252
}
5353

54-
notBefore := time.Now()
55-
notAfter := notBefore.Add(3 * time.Hour)
54+
notBefore, notAfter := makeNotBeforeAndAfter()
5655

5756
rootTemplate := x509.Certificate{
58-
DNSNames: []string{"localhost"},
59-
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
6057
SerialNumber: big.NewInt(1653),
6158
Subject: pkix.Name{
62-
Organization: []string{"Gallifrey"},
63-
CommonName: "localhost",
59+
Country: []string{"Gallifrey"},
60+
Locality: []string{"The Capitol"},
61+
OrganizationalUnit: []string{"Time Lords"},
62+
Organization: []string{"High Council of the Time Lords"},
63+
CommonName: "High Council",
6464
},
6565
NotBefore: notBefore,
6666
NotAfter: notAfter,
@@ -125,16 +125,16 @@ func NewRootCA() (*ecdsa.PrivateKey, *x509.Certificate, Pair, error) {
125125
// If any error occurs during the generation process, a non-nil error is returned.
126126
func GenerateChildCert(name string, ips []net.IP, caPrivKey crypto.PrivateKey, caCert *x509.Certificate) (*tls.Certificate, Pair, error) {
127127

128-
notBefore := time.Now()
129-
notAfter := notBefore.Add(3 * time.Hour)
128+
notBefore, notAfter := makeNotBeforeAndAfter()
130129

131130
certTemplate := &x509.Certificate{
132131
DNSNames: []string{name},
133132
IPAddresses: ips,
134133
SerialNumber: big.NewInt(1658),
135134
Subject: pkix.Name{
136-
Organization: []string{"Gallifrey"},
137-
CommonName: name,
135+
Locality: []string{"anywhere in time and space"},
136+
Organization: []string{"TARDIS"},
137+
CommonName: "Police Public Call Box",
138138
},
139139
NotBefore: notBefore,
140140
NotAfter: notAfter,
@@ -212,3 +212,10 @@ func NewRootAndChildCerts() (Pair, Pair, error) {
212212

213213
return rootPair, childPair, nil
214214
}
215+
216+
func makeNotBeforeAndAfter() (time.Time, time.Time) {
217+
now := time.Now()
218+
notBefore := now.Add(-1 * time.Minute)
219+
notAfter := now.Add(7 * 24 * time.Hour)
220+
return notBefore, notAfter
221+
}

testing/certutil/cmd/main.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,23 @@ package main
1919

2020
import (
2121
"crypto"
22+
"crypto/rand"
2223
"crypto/tls"
2324
"crypto/x509"
25+
"encoding/pem"
2426
"flag"
2527
"fmt"
2628
"net"
2729
"os"
2830
"path/filepath"
2931
"strings"
32+
"time"
3033

3134
"github.com/elastic/elastic-agent-libs/testing/certutil"
3235
)
3336

3437
func main() {
35-
var caPath, caKeyPath, dest, name, ipList string
38+
var caPath, caKeyPath, dest, name, ipList, filePrefix, pass string
3639
flag.StringVar(&caPath, "ca", "",
3740
"File path for CA in PEM format")
3841
flag.StringVar(&caKeyPath, "ca-key", "",
@@ -43,6 +46,10 @@ func main() {
4346
"used as \"distinguished name\" and \"Subject Alternate Name values\" for the child certificate")
4447
flag.StringVar(&ipList, "ips", "127.0.0.1",
4548
"a comma separated list of IP addresses for the child certificate")
49+
flag.StringVar(&filePrefix, "prefix", "current timestamp",
50+
"a prefix to be added to the file name. If not provided a timestamp will be used")
51+
flag.StringVar(&pass, "pass", "",
52+
"a passphrase to encrypt the certificate key")
4653
flag.Parse()
4754

4855
if caPath == "" && caKeyPath != "" || caPath != "" && caKeyPath == "" {
@@ -52,6 +59,16 @@ func main() {
5259
caPath, caKeyPath)
5360

5461
}
62+
if filePrefix == "" {
63+
filePrefix = fmt.Sprintf("%d", time.Now().Unix())
64+
}
65+
filePrefix += "-"
66+
67+
wd, err := os.Getwd()
68+
if err != nil {
69+
fmt.Printf("error getting current working directory: %v\n", err)
70+
}
71+
fmt.Println("files will be witten to:", wd)
5572

5673
ips := strings.Split(ipList, ",")
5774
var netIPs []net.IP
@@ -61,25 +78,57 @@ func main() {
6178

6279
var rootCert *x509.Certificate
6380
var rootKey crypto.PrivateKey
64-
var err error
6581
if caPath == "" && caKeyPath == "" {
6682
var pair certutil.Pair
6783
rootKey, rootCert, pair, err = certutil.NewRootCA()
6884
if err != nil {
6985
panic(fmt.Errorf("could not create root CA certificate: %w", err))
7086
}
7187

72-
savePair(dest, "ca", pair)
88+
savePair(dest, filePrefix+"ca", pair)
7389
} else {
7490
rootKey, rootCert = loadCA(caPath, caKeyPath)
7591
}
7692

77-
_, childPair, err := certutil.GenerateChildCert(name, netIPs, rootKey, rootCert)
93+
childCert, childPair, err := certutil.GenerateChildCert(name, netIPs, rootKey, rootCert)
7894
if err != nil {
7995
panic(fmt.Errorf("error generating child certificate: %w", err))
8096
}
8197

82-
savePair(dest, name, childPair)
98+
savePair(dest, filePrefix+name, childPair)
99+
100+
if pass == "" {
101+
return
102+
}
103+
104+
fmt.Printf("passphrase present, encrypting \"%s\" certificate key\n",
105+
name)
106+
err = os.WriteFile(filePrefix+name+"-passphrase", []byte(pass), 0o600)
107+
if err != nil {
108+
panic(fmt.Errorf("error writing passphrase file: %w", err))
109+
}
110+
111+
key, err := x509.MarshalPKCS8PrivateKey(childCert.PrivateKey)
112+
if err != nil {
113+
panic(fmt.Errorf("error getting ecdh.PrivateKey from the child's private key: %w", err))
114+
}
115+
116+
encPem, err := x509.EncryptPEMBlock( //nolint:staticcheck // we need to drop support for this, but while we don't, it needs to be tested.
117+
rand.Reader,
118+
"EC PRIVATE KEY",
119+
key,
120+
[]byte(pass),
121+
x509.PEMCipherAES128)
122+
if err != nil {
123+
panic(fmt.Errorf("failed encrypting agent child certificate key block: %v", err))
124+
}
125+
126+
certKeyEnc := pem.EncodeToMemory(encPem)
127+
128+
err = os.WriteFile(filepath.Join(dest, filePrefix+name+"_enc-key.pem"), certKeyEnc, 0o600)
129+
if err != nil {
130+
panic(fmt.Errorf("could not save %s certificate encrypted key: %w", filePrefix+name+"_enc-key.pem", err))
131+
}
83132
}
84133

85134
func loadCA(caPath string, keyPath string) (crypto.PrivateKey, *x509.Certificate) {

0 commit comments

Comments
 (0)