Commits

Anonymous committed cf7566b Draft

crypto/tls, http: Make HTTPS servers easier.

R=r, adg, rsc
CC=golang-dev
http://codereview.appspot.com/1684051

Comments (0)

Files changed (3)

src/pkg/crypto/tls/generate_cert.go

+// 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.
+
+// Generate a self-signed X.509 certificate for a TLS server. Outputs to
+// 'cert.pem' and 'key.pem' and will overwrite existing files.
+
+package main
+
+import (
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"fmt"
+	"log"
+	"os"
+	"time"
+)
+
+func main() {
+	if len(os.Args) != 2 {
+		fmt.Printf("Usage: %s <hostname of server>\n", os.Args[0])
+		return
+	}
+
+	hostName := os.Args[1]
+
+	urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
+	if err != nil {
+		log.Crashf("failed to open /dev/urandom: %s\n", err)
+		return
+	}
+
+	log.Stdoutf("Generating RSA key\n")
+	priv, err := rsa.GenerateKey(urandom, 1024)
+	if err != nil {
+		log.Crashf("failed to generate private key: %s\n", err)
+		return
+	}
+
+	now := time.Seconds()
+
+	template := x509.Certificate{
+		SerialNumber: []byte{0},
+		Subject: x509.Name{
+			CommonName:   hostName,
+			Organization: "Acme Co",
+		},
+		NotBefore: time.SecondsToUTC(now - 300),
+		NotAfter:  time.SecondsToUTC(now + 86400*365), // valid for 1 year.
+
+		SubjectKeyId: []byte{1, 2, 3, 4},
+		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+	}
+
+	derBytes, err := x509.CreateCertificate(urandom, &template, &template, &priv.PublicKey, priv)
+	if err != nil {
+		log.Crashf("Failed to create certificate: %s", err)
+		return
+	}
+
+	certOut, err := os.Open("cert.pem", os.O_WRONLY|os.O_CREAT, 0644)
+	if err != nil {
+		log.Crashf("failed to open cert.pem for writing: %s\n", err)
+		return
+	}
+	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+	certOut.Close()
+	log.Stdoutf("written cert.pem\n")
+
+	keyOut, err := os.Open("key.pem", os.O_WRONLY|os.O_CREAT, 0600)
+	if err != nil {
+		log.Crashf("failed to open key.pem for writing: %s\n", err)
+		return
+	}
+	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
+	keyOut.Close()
+	log.Stdoutf("written key.pem\n")
+}

src/pkg/crypto/tls/tls.go

 package tls
 
 import (
+	"io/ioutil"
+	"net"
 	"os"
-	"net"
+	"encoding/pem"
+	"crypto/rsa"
+	"crypto/x509"
 )
 
 func Server(conn net.Conn, config *Config) *Conn {
 	}
 	return Client(c, nil), nil
 }
+
+// LoadX509KeyPair
+func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) {
+	certPEMBlock, err := ioutil.ReadFile(certFile)
+	if err != nil {
+		return
+	}
+
+	certDERBlock, _ := pem.Decode(certPEMBlock)
+	if certDERBlock == nil {
+		err = os.ErrorString("failed to parse certificate PEM data")
+		return
+	}
+
+	cert.Certificate = [][]byte{certDERBlock.Bytes}
+
+	keyPEMBlock, err := ioutil.ReadFile(keyFile)
+	if err != nil {
+		return
+	}
+
+	keyDERBlock, _ := pem.Decode(keyPEMBlock)
+	if keyDERBlock == nil {
+		err = os.ErrorString("failed to parse key PEM data")
+		return
+	}
+
+	key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
+	if err != nil {
+		err = os.ErrorString("failed to parse key")
+		return
+	}
+
+	cert.PrivateKey = key
+
+	// We don't need to parse the public key for TLS, but we so do anyway
+	// to check that it looks sane and matches the private key.
+	x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
+	if err != nil {
+		return
+	}
+
+	if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
+		err = os.ErrorString("Private key does not match public key")
+		return
+	}
+
+	return
+}

src/pkg/http/server.go

 
 import (
 	"bufio"
+	"crypto/rand"
+	"crypto/tls"
 	"fmt"
 	"io"
 	"log"
 	"path"
 	"strconv"
 	"strings"
+	"time"
 )
 
 // Errors introduced by the HTTP server.
 	l.Close()
 	return e
 }
+
+// ListenAndServeTLS acts identically to ListenAndServe, expect that it
+// except HTTPS connections. Additionally, files containing a certificate and
+// matching private key for the server must be provided.
+//
+// A trivial example server is:
+//
+//	import (
+//		"http"
+//		"log"
+//	)
+//
+//	func handler(conn *http.Conn, req *http.Request) {
+//		conn.SetHeader("Content-Type", "text/plain")
+//		conn.Write([]byte("This is an example server.\n"))
+//	}
+//
+//	func main() {
+//		http.HandleFunc("/", handler)
+//		log.Stdoutf("About to listen on 10443. Go to https://127.0.0.1:10443/")
+//		err := http.ListenAndServe(":10443", "cert.pem", "key.pem", nil)
+//		if err != nil {
+//			log.Exit(err)
+//		}
+//	}
+//
+// One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem.
+func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) os.Error {
+	config := &tls.Config{
+		Rand:       rand.Reader,
+		Time:       time.Seconds,
+		NextProtos: []string{"http/1.1"},
+	}
+
+	var err os.Error
+	config.Certificates = make([]tls.Certificate, 1)
+	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		return err
+	}
+
+	conn, err := net.Listen("tcp", addr)
+	if err != nil {
+		return err
+	}
+
+	tlsListener := tls.NewListener(conn, config)
+	return Serve(tlsListener, handler)
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.