From aec17304a0ae59bdea73b82dba742f8d7152e8d2 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Fri, 10 Jan 2014 16:59:54 -0500 Subject: [PATCH] Add standalone gencerts utility to create RPC TLS certificates. --- util/gencerts/gencerts.go | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 util/gencerts/gencerts.go diff --git a/util/gencerts/gencerts.go b/util/gencerts/gencerts.go new file mode 100644 index 00000000..b6407ee2 --- /dev/null +++ b/util/gencerts/gencerts.go @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "github.com/conformal/btcutil" + "github.com/conformal/go-flags" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +type config struct { + Directory string `short:"d" long:"directory" description:"Directory to write certificate pair"` + Years int `short:"y" long:"years" description:"How many years a certificate is valid for"` + Organization string `short:"o" long:"org" description:"Organization in certificate"` + ExtraHosts []string `short:"H" long:"host" description:"Additional hosts/IPs to create certificate for"` + Force bool `short:"f" long:"force" description:"Force overwriting of any old certs and keys"` +} + +func main() { + cfg := config{ + Years: 10, + Organization: "gencerts", + } + parser := flags.NewParser(&cfg, flags.Default) + _, err := parser.Parse() + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + parser.WriteHelp(os.Stderr) + } + return + } + + if cfg.Directory == "" { + var err error + cfg.Directory, err = os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "No directory specified and Cannot get working directory\n") + os.Exit(1) + } + } + cfg.Directory = cleanAndExpandPath(cfg.Directory) + certFile := filepath.Join(cfg.Directory, "rpc.cert") + keyFile := filepath.Join(cfg.Directory, "rpc.key") + + if !cfg.Force { + if fileExists(certFile) || fileExists(keyFile) { + fmt.Fprintf(os.Stderr, "%v: certificate and/or key files exist; use -f to force\n", cfg.Directory) + os.Exit(1) + } + } + + validUntil := time.Now().Add(time.Duration(cfg.Years) * 365 * 24 * time.Hour) + cert, key, err := btcutil.NewTLSCertPair(cfg.Organization, validUntil, cfg.ExtraHosts) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot generate certificate pair: %v\n", err) + os.Exit(1) + } + + // Write cert and key files. + if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { + fmt.Fprintf(os.Stderr, "cannot write cert: %v\n", err) + os.Exit(1) + } + if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { + os.Remove(certFile) + fmt.Fprintf(os.Stderr, "cannot write key: %v\n", err) + os.Exit(1) + } +} + +// cleanAndExpandPath expands environement variables and leading ~ in the +// passed path, cleans the result, and returns it. +func cleanAndExpandPath(path string) string { + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + appHomeDir := btcutil.AppDataDir("gencerts", false) + homeDir := filepath.Dir(appHomeDir) + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but they variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} + +// filesExists reports whether the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +}