Skip to content

Instantly share code, notes, and snippets.

@shivakar
Created December 12, 2015 17:16
Show Gist options
  • Save shivakar/cd52b5594d4912fbeb46 to your computer and use it in GitHub Desktop.
Save shivakar/cd52b5594d4912fbeb46 to your computer and use it in GitHub Desktop.
TLS server with in-memory self-signed certificate
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"log"
"math/big"
"net"
"net/http"
"os"
"time"
)
// From https://golang.org/src/net/http/server.go
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
// GenX509KeyPair generates the TLS keypair for the server
func GenX509KeyPair() (tls.Certificate, error) {
now := time.Now()
template := &x509.Certificate{
SerialNumber: big.NewInt(now.Unix()),
Subject: pkix.Name{
CommonName: "quickserve.example.com",
Country: []string{"USA"},
Organization: []string{"example.com"},
OrganizationalUnit: []string{"quickserve"},
},
NotBefore: now,
NotAfter: now.AddDate(0, 0, 1), // Valid for one day
SubjectKeyId: []byte{113, 117, 105, 99, 107, 115, 101, 114, 118, 101},
BasicConstraintsValid: true,
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
}
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return tls.Certificate{}, err
}
cert, err := x509.CreateCertificate(rand.Reader, template, template,
priv.Public(), priv)
if err != nil {
return tls.Certificate{}, err
}
var outCert tls.Certificate
outCert.Certificate = append(outCert.Certificate, cert)
outCert.PrivateKey = priv
return outCert, nil
}
// Usage prints the usage string
func Usage() {
l := log.New(os.Stderr, "", 0)
l.Fatalf("Usage: %s <directory-to-serve>\n", os.Args[0])
}
// ListenAndServeTLSKeyPair start a server using in-memory TLS KeyPair
func ListenAndServeTLSKeyPair(addr string, cert tls.Certificate,
handler http.Handler) error {
if addr == "" {
return errors.New("Invalid address string")
}
server := &http.Server{Addr: addr, Handler: handler}
config := &tls.Config{}
config.NextProtos = []string{"http/1.1"}
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0] = cert
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)},
config)
return server.Serve(tlsListener)
}
func main() {
if len(os.Args) < 2 {
Usage()
}
cert, err := GenX509KeyPair()
if err != nil {
log.Fatalln(err)
}
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir(os.Args[1])))
log.Println("Starting server at https://127.0.0.1:8080/")
err = ListenAndServeTLSKeyPair("127.0.0.1:8080", cert, mux)
if err != nil {
log.Fatalln(err)
}
}
@willstott101
Copy link

For those still finding this on google, there is a simpler and slightly more up to date Q/A on StackOverflow: https://stackoverflow.com/questions/47857573/passing-certificate-and-key-as-string-to-listenandservetls/47857805

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment