From 57a8603c662e455281f659985af2923f89e89abe Mon Sep 17 00:00:00 2001 From: Malte Swart Date: Tue, 2 Sep 2025 09:03:57 +0200 Subject: [PATCH] Add option to reuse key material It needs to be opt-in via a command-line option. The code currently does not check whether the read key has the requested algorithm / key size. --- main.go | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index ffbc2d0..5fbb3b7 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ type issuer struct { cert *x509.Certificate } -func getIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm) (*issuer, error) { +func getIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm, reuseKey bool) (*issuer, error) { keyContents, keyErr := ioutil.ReadFile(keyFile) certContents, certErr := ioutil.ReadFile(certFile) if os.IsNotExist(keyErr) && os.IsNotExist(certErr) { @@ -46,10 +46,21 @@ func getIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm) (*issuer, if err != nil { return nil, err } - return getIssuer(keyFile, certFile, alg) + return getIssuer(keyFile, certFile, alg, false) } else if keyErr != nil { return nil, fmt.Errorf("%s (but %s exists)", keyErr, certFile) } else if certErr != nil { + if reuseKey { + key, err := readPrivateKey(keyContents) + if err != nil { + return nil, fmt.Errorf("reading private key from %s: %s", keyFile, err) + } + _, err = makeRootCert(key, certFile) + if err != nil { + return nil, err + } + return getIssuer(keyFile, certFile, alg, false) + } return nil, fmt.Errorf("%s (but %s exists)", certErr, keyFile) } key, err := readPrivateKey(keyContents) @@ -240,7 +251,7 @@ func calculateSKID(pubKey crypto.PublicKey) ([]byte, error) { return skid[:], nil } -func sign(iss *issuer, domains []string, ipAddresses []string, alg x509.PublicKeyAlgorithm) (*x509.Certificate, error) { +func sign(iss *issuer, domains []string, ipAddresses []string, alg x509.PublicKeyAlgorithm, reuseKey bool) (*x509.Certificate, error) { var cn string if len(domains) > 0 { cn = domains[0] @@ -254,9 +265,22 @@ func sign(iss *issuer, domains []string, ipAddresses []string, alg x509.PublicKe if err != nil && !os.IsExist(err) { return nil, err } - key, err := makeKey(fmt.Sprintf("%s/key.pem", cnFolder), alg) - if err != nil { - return nil, err + var keyFile = fmt.Sprintf("%s/key.pem", cnFolder) + var key crypto.Signer + if reuseKey { + keyContents, keyErr := ioutil.ReadFile(keyFile) + if keyErr == nil { + key, err = readPrivateKey(keyContents) + if err != nil { + return nil, fmt.Errorf("reading private key from %s: %s", keyFile, err) + } + } + } + if key == nil { + key, err = makeKey(keyFile, alg) + if err != nil { + return nil, err + } } parsedIPs, err := parseIPs(ipAddresses) if err != nil { @@ -315,6 +339,7 @@ func main2() error { var caKey = flag.String("ca-key", "minica-key.pem", "Root private key filename, PEM encoded.") var caCert = flag.String("ca-cert", "minica.pem", "Root certificate filename, PEM encoded.") var caAlg = flag.String("ca-alg", "ecdsa", "Algorithm for any new keypairs: RSA or ECDSA.") + var reuseKeys = flag.Bool("reuse-keys", false, "If only the key file exists, reuse it to generate the certificate") var domains = flag.String("domains", "", "Comma separated domain names to include as Server Alternative Names.") var ipAddresses = flag.String("ip-addresses", "", "Comma separated IP addresses to include as Server Alternative Names.") flag.Usage = func() { @@ -371,10 +396,10 @@ will not overwrite existing keys or certificates. os.Exit(1) } } - issuer, err := getIssuer(*caKey, *caCert, alg) + issuer, err := getIssuer(*caKey, *caCert, alg, *reuseKeys) if err != nil { return err } - _, err = sign(issuer, domainSlice, ipSlice, alg) + _, err = sign(issuer, domainSlice, ipSlice, alg, *reuseKeys) return err }