diff --git a/Makefile b/Makefile index acba41f..e76fe9f 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,8 @@ GOFILES = \ group.go \ user.go \ murmurdb.go \ - freeze.go + freeze.go \ + gencert.go .PHONY: grumble grumble: pkg diff --git a/gencert.go b/gencert.go new file mode 100644 index 0000000..fec8208 --- /dev/null +++ b/gencert.go @@ -0,0 +1,79 @@ +// Copyright (c) 2011 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "log" + "os" + "path/filepath" + "time" +) + +// Generate a 2048-bit RSA keypair and a Grumble auto-generated X509 +// certificate. Output PEM-encoded DER representations of the resulting +// certificate and private key to certpath and keypath. +func GenerateSelfSignedCert(certpath, keypath string) (err os.Error) { + now := time.Seconds() + tmpl := &x509.Certificate{ + SerialNumber: []byte{0}, + Subject: x509.Name{ + CommonName: "Grumble Autogenerated Certificate", + }, + NotBefore: time.SecondsToUTC(now - 300), + NotAfter: time.SecondsToUTC(now + 60*60*24*365), // valid for 1 year. + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return err + } + + certbuf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &priv.PublicKey, priv) + if err != nil { + log.Printf("Error: %v", err) + return err + } + certblk := pem.Block{ + Type: "CERTIFICATE", + Bytes: certbuf, + } + + keybuf := x509.MarshalPKCS1PrivateKey(priv) + keyblk := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: keybuf, + } + + certfn := filepath.Join(*datadir, "cert") + file, err := os.OpenFile(certfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) + if err != nil { + return err + } + defer file.Close() + err = pem.Encode(file, &certblk) + if err != nil { + return err + } + + keyfn := filepath.Join(*datadir, "key") + file, err = os.OpenFile(keyfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) + if err != nil { + return err + } + defer file.Close() + err = pem.Encode(file, &keyblk) + if err != nil { + return err + } + + return nil +} diff --git a/grumble.go b/grumble.go index 02379ca..d898fc1 100644 --- a/grumble.go +++ b/grumble.go @@ -24,6 +24,7 @@ var datadir *string = flag.String("datadir", "", "Directory to use for server st var blobdir *string = flag.String("blobdir", "", "Directory to use for blob storage") var sqlitedb *string = flag.String("murmurdb", "", "Path to murmur.sqlite to import server structure from") var cleanup *bool = flag.Bool("clean", false, "Clean up existing data dir content before importing Murmur data") +var gencert *bool = flag.Bool("gencert", false, "Generate a self-signed certificate for use with Grumble") var globalBlobstore *blobstore.BlobStore @@ -94,6 +95,26 @@ func main() { log.Fatalf("Unable to initialize blobstore: %v", err.String()) } + // Generate a cert? + if *gencert { + certfn := filepath.Join(*datadir, "cert") + keyfn := filepath.Join(*datadir, "key") + log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...") + + err := GenerateSelfSignedCert(certfn, keyfn) + if err != nil { + log.Printf("Error: %v", err) + return + } + + log.Printf("Certificate output to %v", certfn) + log.Printf("Private key output to %v", keyfn) + + log.Printf("Done generating certificate and private key.") + log.Printf("Please restart Grumble to make use of the generated certificate and private key.") + return + } + // Should we import data from a Murmur SQLite file? if len(*sqlitedb) > 0 { f, err := os.Open(*datadir) diff --git a/tlsserver.go b/tlsserver.go index b68b38d..25b548a 100644 --- a/tlsserver.go +++ b/tlsserver.go @@ -9,13 +9,14 @@ import ( "encoding/pem" "crypto/x509" "io/ioutil" + "path/filepath" ) func NewTLSListener(port int) (rl *tls.Listener) { rl = nil // Load the certificate - pemBytes, err := ioutil.ReadFile("grumble.crt") + pemBytes, err := ioutil.ReadFile(filepath.Join(*datadir, "cert")) if err != nil { log.Printf("Failed to read server.crt: %s", err) return @@ -29,7 +30,7 @@ func NewTLSListener(port int) (rl *tls.Listener) { } // Load the private key - keyBytes, err := ioutil.ReadFile("grumble.key") + keyBytes, err := ioutil.ReadFile(filepath.Join(*datadir, "key")) if err != nil { log.Printf("Failed to read server.key.insecure: %s", err) return