package utils import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "log" "math/big" "net" "os" "path/filepath" "time" ) func GenerateCerts(tlsCert string, tlsKey string) { // @see https://shaneutt.com/blog/golang-ca-and-signed-cert-go/ // @see https://golang.org/src/crypto/tls/generate_cert.go validFrom := "" validFor := 365 * 24 * time.Hour isCA := true // Get the hostname hostname, err := os.Hostname() if err != nil { log.Printf("failed to lookup hostname: '%s'\n", err) hostname = "localhost" } // Check that the directory exists relativePath := filepath.Dir(tlsCert) log.Printf("GenerateCerts relative path for file creation is '%s'\n", relativePath) _, err = os.Stat(relativePath) if os.IsNotExist(err) { log.Printf("Certificate path does not exist, creating %s before generating certificate\n", relativePath) os.MkdirAll(relativePath, os.ModePerm) } // Generate a private key priv, err := rsa.GenerateKey(rand.Reader, rsaBits) if err != nil { log.Fatalf("Failed to generate private key: %v", err) } 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 { log.Fatalf("Failed to parse creation date: %v", err) } } notAfter := notBefore.Add(validFor) 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: %v", err) } template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"DTMS"}, }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } template.DNSNames = append(template.DNSNames, hostname) // Add in all the non-local IPs ifaces, err := net.Interfaces() if err != nil { log.Printf("Error enumerating interfaces: %v\n", err) } for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { log.Printf("Oops: %v\n", err) } for _, address := range addrs { // check the address type and if it is not a loopback then add it to the list if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { template.IPAddresses = append(template.IPAddresses, ipnet.IP) } } } } if isCA { template.IsCA = true template.KeyUsage |= x509.KeyUsageCertSign } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { log.Fatalf("Failed to create certificate: %v", err) } certOut, err := os.Create(tlsCert) if err != nil { log.Fatalf("Failed to open %s for writing: %v", tlsCert, err) } if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { log.Fatalf("Failed to write data to %s: %v", tlsCert, err) } if err := certOut.Close(); err != nil { log.Fatalf("Error closing %s: %v", tlsCert, err) } log.Printf("wrote %s\n", tlsCert) keyOut, err := os.OpenFile(tlsKey, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Fatalf("Failed to open %s for writing: %v", tlsKey, err) return } privBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { log.Fatalf("Unable to marshal private key: %v", err) } if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { log.Fatalf("Failed to write data to %s: %v", tlsKey, err) } if err := keyOut.Close(); err != nil { log.Fatalf("Error closing %s: %v", tlsKey, err) } log.Printf("wrote %s\n", tlsKey) }