author | Alberto Bertogli
<albertito@blitiri.com.ar> 2022-08-27 22:27:15 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2022-08-27 22:39:40 UTC |
parent | 21e8d50df6310295c7488c72f67a0b8008e6af4c |
test/util/generate_cert/generate_cert.go | +71 | -110 |
test/util/lib.sh | +1 | -1 |
diff --git a/test/util/generate_cert/generate_cert.go b/test/util/generate_cert/generate_cert.go index 1fe2249..e3e0b6f 100644 --- a/test/util/generate_cert/generate_cert.go +++ b/test/util/generate_cert/generate_cert.go @@ -1,26 +1,21 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - //go:build !coverage // +build !coverage -// Generate a self-signed X.509 certificate for a TLS server. Outputs to -// 'cert.pem' and 'key.pem' and will overwrite existing files. - +// Utility to generate self-signed certificates. +// It generates a self-signed x509 certificate and key pair, and writes them +// to "fullchain.pem" and "privkey.pem". +// +// Intended for use in tests, not for production use. package main import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" + crand "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "flag" "fmt" - "log" "math/big" "net" "os" @@ -31,138 +26,104 @@ import ( ) var ( - host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") - validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") - validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") - isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") - rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set") - ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") + host = flag.String("host", "", + "Hostnames/IPs to generate the certificate for (comma separated)") + validFor = flag.Duration("validfor", 4*time.Hour, + "How long will the certificate be valid for") + isCA = flag.Bool("ca", false, + "Should this cert be its own CA?") ) -func publicKey(priv interface{}) interface{} { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - default: - return nil - } -} - -func pemBlockForKey(priv interface{}) *pem.Block { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} - case *ecdsa.PrivateKey: - b, err := x509.MarshalECPrivateKey(k) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) - os.Exit(2) - } - return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} - default: - return nil - } +func fatalf(f string, a ...interface{}) { + fmt.Printf(f, a...) + os.Exit(1) } func main() { flag.Parse() - - if len(*host) == 0 { - log.Fatalf("Missing required --host parameter") + if *host == "" { + fatalf("Required flag: --host") } - var priv interface{} - var err error - switch *ecdsaCurve { - case "": - priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) - case "P224": - priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case "P256": - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case "P384": - priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case "P521": - priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - default: - fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) - os.Exit(1) - } + // Build the certificate template. + serial, err := crand.Int(crand.Reader, big.NewInt(1<<62)) if err != nil { - log.Fatalf("failed to generate private key: %s", err) + fatalf("Error generating serial number: %v\n", err) } + tmpl := x509.Certificate{ + SerialNumber: serial, + Subject: pkix.Name{Organization: []string{"Test Cert Org"}}, - var notBefore time.Time - if len(*validFrom) == 0 { - notBefore = time.Now() - } else { - notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) - os.Exit(1) - } - } + // Valid from now until `--validfor` in the future. + // Extended certs can be useful for manual troubleshooting. + NotBefore: time.Now(), + NotAfter: time.Now().Add(*validFor), - notAfter := notBefore.Add(*validFor) + KeyUsage: x509.KeyUsageKeyEncipherment | + x509.KeyUsageDigitalSignature | + x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - log.Fatalf("failed to generate serial number: %s", err) + BasicConstraintsValid: true, } - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, + if *isCA { + tmpl.IsCA = true } hosts := strings.Split(*host, ",") for _, h := range hosts { if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) + tmpl.IPAddresses = append(tmpl.IPAddresses, ip) } else { // We use IDNA-encoded DNS names, otherwise the TLS library won't // load the certificates. ih, err := idna.ToASCII(h) if err != nil { - log.Fatalf("host %q cannot be IDNA-encoded: %v", h, err) + fatalf("Host %q cannot be IDNA-encoded: %v\n", h, err) } - template.DNSNames = append(template.DNSNames, ih) + tmpl.DNSNames = append(tmpl.DNSNames, ih) } } - if *isCA { - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + // Generate a private key (RSA 2048). + privK, err := rsa.GenerateKey(crand.Reader, 2048) if err != nil { - log.Fatalf("Failed to create certificate: %s", err) + fatalf("Error generating key: %v\n", err) } - certOut, err := os.Create("fullchain.pem") - if err != nil { - log.Fatalf("failed to open fullchain.pem for writing: %s", err) + // Write the certificate. + { + derBytes, err := x509.CreateCertificate( + crand.Reader, &tmpl, &tmpl, &privK.PublicKey, privK) + if err != nil { + fatalf("Failed to create certificate: %v\n", err) + } + + fullchain, err := os.Create("fullchain.pem") + if err != nil { + fatalf("Failed to open fullchain.pem: %v\n", err) + } + err = pem.Encode(fullchain, + &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + fatalf("Error encoding certificate: %v\n", err) + } + fullchain.Close() } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - certOut.Close() - keyOut, err := os.OpenFile("privkey.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Fatalf("failed to open privkey.pem for writing: %s", err) - return + // Write the private key. + { + privkey, err := os.Create("privkey.pem") + if err != nil { + fatalf("failed to open privkey.pem: %v\n", err) + } + block := &pem.Block{Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privK)} + err = pem.Encode(privkey, block) + if err != nil { + fatalf("Error encoding private key: %v\n", err) + } + privkey.Close() } - pem.Encode(keyOut, pemBlockForKey(priv)) - keyOut.Close() } diff --git a/test/util/lib.sh b/test/util/lib.sh index 560de12..4584460 100644 --- a/test/util/lib.sh +++ b/test/util/lib.sh @@ -186,7 +186,7 @@ function generate_certs_for() { mkdir -p ${CONFDIR}/certs/${1}/ ( cd ${CONFDIR}/certs/${1} - generate_cert -ca -duration=1h -host=${1} + generate_cert -ca -validfor=1h -host=${1} ) }