From 970fd9f761918bd6014e83573e081f1a98458c75 Mon Sep 17 00:00:00 2001 From: gerald1248 Date: Tue, 14 Mar 2017 09:11:23 +0100 Subject: [PATCH] added support for diskless creation of TLS key pairs --- README.md | 100 +++++++++++++++++++++++++++++++++++++++----------- httpscerts.go | 38 ++++++++++++------- 2 files changed, 104 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 07c6f79..20ad523 100644 --- a/README.md +++ b/README.md @@ -7,30 +7,88 @@ Use this library for testing purposes only, e.g. to experiment with the built-in # Usage - - package main +```go +package main - import ( - "fmt" - "github.com/kabukky/httpscerts" - "log" - "net/http" - ) +import ( + "fmt" + "github.com/kabukky/httpscerts" + "log" + "net/http" +) - func handler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hi there!") - } +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hi there!") +} - func main() { - // Check if the cert files are available. - err := httpscerts.Check("cert.pem", "key.pem") - // If they are not available, generate new ones. +func main() { + // Check if the cert files are available. + err := httpscerts.Check("cert.pem", "key.pem") + // If they are not available, generate new ones. + if err != nil { + err = httpscerts.Generate("cert.pem", "key.pem", "127.0.0.1:8081") if err != nil { - err = httpscerts.Generate("cert.pem", "key.pem", "127.0.0.1:8081") - if err != nil { - log.Fatal("Error: Couldn't create https certs.") - } + log.Fatal("Error: Couldn't create https certs.") } - http.HandleFunc("/", handler) - http.ListenAndServeTLS(":8081", "cert.pem", "key.pem", nil) } + http.HandleFunc("/", handler) + http.ListenAndServeTLS(":8081", "cert.pem", "key.pem", nil) +} +``` + +# Alternative usage without disk access + +The method `httpscerts.GenerateArrays()` has been added to enable use cases where writing to disk is not desirable. If the initial check fails, a `tls.Certificate` is populated and passed to a `http.Server` instance. + +```go +package main + +import ( + "crypto/tls" + "fmt" + "github.com/kabukky/httpscerts" + "log" + "net/http" + "time" +) + +func main() { + // Check if the cert files are available. + certFile := "cert.pem" + keyFile := "key.pem" + err := httpscerts.Check(certFile, keyFile) + // If they are not available, generate new ones. + if err != nil { + cert, key, err := httpscerts.GenerateArrays("127.0.0.1:8081") + if err != nil { + log.Fatal("Error: Couldn't create https certs.") + } + + keyPair, err := tls.X509KeyPair(cert, key) + if err != nil { + log.Fatal("Error: Couldn't create key pair") + } + + var certificates []tls.Certificate + certificates = append(certificates, keyPair) + + cfg := &tls.Config{ + MinVersion: tls.VersionTLS12, + PreferServerCipherSuites: true, + Certificates: certificates, + } + + s := &http.Server{ + Addr: ":8081", + Handler: http.DefaultServeMux, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + TLSConfig: cfg, + } + log.Fatal(s.ListenAndServeTLS("", "")) + } + + log.Fatal(http.ListenAndServeTLS(":8081", certFile, keyFile, nil)) +} +``` diff --git a/httpscerts.go b/httpscerts.go index 6aa0304..90b6190 100644 --- a/httpscerts.go +++ b/httpscerts.go @@ -18,6 +18,7 @@ import ( "crypto/x509/pkix" "encoding/pem" "fmt" + "io/ioutil" "log" "math/big" "net" @@ -70,7 +71,10 @@ func Check(certPath string, keyPath string) error { return nil } -func Generate(certPath string, keyPath string, host string) error { +//generate cert and key byte arrays +//these can then be used to populate the server configuration +//in place of files on disk +func GenerateArrays(host string) ([]byte, []byte, error) { var priv interface{} var err error switch ecdsaCurve { @@ -90,7 +94,7 @@ func Generate(certPath string, keyPath string, host string) error { } if err != nil { log.Printf("failed to generate private key: %s", err) - return err + return nil, nil, err } var notBefore time.Time @@ -100,7 +104,7 @@ func Generate(certPath string, keyPath string, host string) error { 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) - return err + return nil, nil, err } } @@ -110,7 +114,7 @@ func Generate(certPath string, keyPath string, host string) error { serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { log.Printf("failed to generate serial number: %s", err) - return err + return nil, nil, err } template := x509.Certificate{ @@ -143,25 +147,33 @@ func Generate(certPath string, keyPath string, host string) error { derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) if err != nil { log.Printf("Failed to create certificate: %s", err) + return nil, nil, err + } + + certArray := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + keyArray := pem.EncodeToMemory(pemBlockForKey(priv)) + + return certArray, keyArray, nil +} + +func Generate(certPath, keyPath, host string) error { + certArray, keyArray, err := GenerateArrays(host) + if err != nil { return err } - certOut, err := os.Create(certPath) + err = ioutil.WriteFile(certPath, certArray, 0600) if err != nil { log.Printf("failed to open "+certPath+" for writing: %s", err) - return err + return err } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - certOut.Close() log.Print("written cert.pem\n") - keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + err = ioutil.WriteFile(keyPath, keyArray, 0600) if err != nil { - log.Print("failed to open "+keyPath+" for writing:", err) - return err + log.Printf("failed to open "+keyPath+" for writing: %s", err) } - pem.Encode(keyOut, pemBlockForKey(priv)) - keyOut.Close() log.Print("written key.pem\n") + return nil }