Last active
December 15, 2019 22:19
-
-
Save gabriel-samfira/61663ec3c07652d4deeeccfdec319d64 to your computer and use it in GitHub Desktop.
Generate certificates for quick testing. This generates a CA, server cert and client cert. The CA allows client certificate verification. By default local hostname and IPs are added to the certificate. Use -certificate-hosts to add a comma separated list of aditional hosts/ips.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/ecdsa" | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/x509" | |
"crypto/x509/pkix" | |
"encoding/pem" | |
"flag" | |
"fmt" | |
"log" | |
"math/big" | |
"net" | |
"os" | |
"path/filepath" | |
"strings" | |
"time" | |
) | |
var certHostsHelpMsg = `Extra hosts to set as DNS names/IP addresses in the certificate.` | |
var ( | |
outDir = flag.String("output-dir", "", "output dir for certificates") | |
hosts = flag.String("certificate-hosts", "", certHostsHelpMsg) | |
) | |
func getLocalIPAddresses() ([]net.IP, error) { | |
ifaces, err := net.Interfaces() | |
if err != nil { | |
return nil, err | |
} | |
ret := []net.IP{} | |
for _, i := range ifaces { | |
addrs, err := i.Addrs() | |
if err != nil { | |
return nil, err | |
} | |
for _, addr := range addrs { | |
var ip net.IP | |
switch v := addr.(type) { | |
case *net.IPNet: | |
ip = v.IP | |
case *net.IPAddr: | |
ip = v.IP | |
} | |
if ip == nil || ip.IsLoopback() { | |
continue | |
} | |
ret = append(ret, ip) | |
} | |
} | |
return ret, nil | |
} | |
// shamelessly copied from https://golang.org/src/crypto/tls/generate_cert.go | |
func publicKey(priv interface{}) interface{} { | |
switch k := priv.(type) { | |
case *rsa.PrivateKey: | |
return &k.PublicKey | |
case *ecdsa.PrivateKey: | |
return &k.PublicKey | |
default: | |
return nil | |
} | |
} | |
// shamelessly copied from https://golang.org/src/crypto/tls/generate_cert.go | |
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 writeKeyFile(path string, key interface{}) error { | |
keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | |
if err != nil { | |
return fmt.Errorf("failed to open %s for writing:", path, err) | |
} | |
if err := pem.Encode(keyOut, pemBlockForKey(key)); err != nil { | |
return fmt.Errorf("failed to write data to %s: %s", path, err) | |
} | |
if err := keyOut.Close(); err != nil { | |
return fmt.Errorf("error closing %s: %s", path, err) | |
} | |
return nil | |
} | |
func writeCertFile(path string, certBytes []byte) error { | |
certOut, err := os.Create(path) | |
if err != nil { | |
return fmt.Errorf("failed to open %s for writing: %s", path, err) | |
} | |
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { | |
return fmt.Errorf("failed to write data to %s: %s", path, err) | |
} | |
if err := certOut.Close(); err != nil { | |
return fmt.Errorf("error closing %s: %s", path, err) | |
} | |
return nil | |
} | |
func generateCertificates(rootKey, rootCert, srvKey, srvCert, cliKey, cliCert string, extraHosts []string) (err error) { | |
defer func() { | |
if err != nil { | |
os.Remove(rootKey) | |
os.Remove(rootCert) | |
os.Remove(srvKey) | |
os.Remove(srvCert) | |
os.Remove(cliKey) | |
os.Remove(cliCert) | |
} | |
}() | |
localIps, err := getLocalIPAddresses() | |
if err != nil { | |
return err | |
} | |
hostname, err := os.Hostname() | |
if err != nil { | |
return err | |
} | |
var root interface{} | |
root, err = rsa.GenerateKey(rand.Reader, 2048) | |
if err != nil { | |
return err | |
} | |
if err := writeKeyFile(rootKey, root); err != nil { | |
return err | |
} | |
now := time.Now() | |
notBefore := now.Add(time.Duration(-24) * time.Hour) | |
notAfter := notBefore.Add(87600 * time.Hour) // 10 years | |
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | |
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | |
if err != nil { | |
return fmt.Errorf("failed to generate serial number: %s", err) | |
} | |
rootTemplate := x509.Certificate{ | |
SerialNumber: serialNumber, | |
Subject: pkix.Name{ | |
Organization: []string{"Cloudbase Solutions"}, | |
CommonName: "Root CA", | |
}, | |
NotBefore: notBefore, | |
NotAfter: notAfter, | |
KeyUsage: x509.KeyUsageCertSign, | |
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, | |
BasicConstraintsValid: true, | |
IsCA: true, | |
} | |
derBytes, err := x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, publicKey(root), root) | |
if err != nil { | |
return err | |
} | |
if err := writeCertFile(rootCert, derBytes); err != nil { | |
return err | |
} | |
var priv interface{} | |
priv, err = rsa.GenerateKey(rand.Reader, 2048) | |
if err != nil { | |
return err | |
} | |
if err := writeKeyFile(srvKey, priv); err != nil { | |
return err | |
} | |
leafSerialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | |
if err != nil { | |
return fmt.Errorf("failed to generate serial number: %s", err) | |
} | |
dnsNames := []string{hostname, fmt.Sprintf("%s.local", hostname), "localhost"} | |
for _, val := range extraHosts { | |
if ip := net.ParseIP(val); ip == nil { | |
dnsNames = append(dnsNames, val) | |
} else { | |
localIps = append(localIps, ip) | |
} | |
} | |
leafTemplate := x509.Certificate{ | |
SerialNumber: leafSerialNumber, | |
Subject: pkix.Name{ | |
Organization: []string{"Cloudbase Solutions"}, | |
CommonName: "replicator", | |
}, | |
NotBefore: notBefore, | |
NotAfter: notAfter, | |
IPAddresses: localIps, | |
DNSNames: dnsNames, | |
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, | |
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | |
BasicConstraintsValid: true, | |
IsCA: false, | |
} | |
derBytes, err = x509.CreateCertificate(rand.Reader, &leafTemplate, &rootTemplate, publicKey(priv), root) | |
if err != nil { | |
return fmt.Errorf("Failed to create certificate: %s", err) | |
} | |
if err := writeCertFile(srvCert, derBytes); err != nil { | |
return err | |
} | |
var clientKey interface{} | |
clientKey, err = rsa.GenerateKey(rand.Reader, 2048) | |
if err != nil { | |
return err | |
} | |
if err := writeKeyFile(cliKey, clientKey); err != nil { | |
return err | |
} | |
clientTemplate := x509.Certificate{ | |
SerialNumber: new(big.Int).SetInt64(4), | |
Subject: pkix.Name{ | |
Organization: []string{"Cloudbase Solutions"}, | |
CommonName: "replicator_client", | |
}, | |
NotBefore: notBefore, | |
NotAfter: notAfter, | |
KeyUsage: x509.KeyUsageDigitalSignature, | |
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, | |
BasicConstraintsValid: true, | |
IsCA: false, | |
} | |
derBytes, err = x509.CreateCertificate(rand.Reader, &clientTemplate, &rootTemplate, publicKey(clientKey), root) | |
if err != nil { | |
return err | |
} | |
if err := writeCertFile(cliCert, derBytes); err != nil { | |
return err | |
} | |
return nil | |
} | |
func main() { | |
flag.Parse() | |
var err error | |
if *outDir == "" { | |
*outDir, err = os.Getwd() | |
if err != nil { | |
log.Fatalf("failed to get cwd: %q", err) | |
} | |
} | |
var certHosts []string | |
if *hosts != "" { | |
certHosts = strings.Split( | |
strings.Replace(*hosts, " ", "", -1), ",") | |
} | |
caPrivKey := filepath.Join(*outDir, "ca-key.pem") | |
caPubKey := filepath.Join(*outDir, "ca-pub.pem") | |
srvPrivKey := filepath.Join(*outDir, "srv-key.pem") | |
srvPubKey := filepath.Join(*outDir, "srv-pub.pem") | |
clientPrivKey := filepath.Join(*outDir, "client-key.pem") | |
clientPubKey := filepath.Join(*outDir, "client-pub.pem") | |
if err := generateCertificates(caPrivKey, caPubKey, srvPrivKey, srvPubKey, clientPrivKey, clientPubKey, certHosts); err != nil { | |
log.Fatalf("error generating certificates: %q", err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment