diff --git a/generate_nmc_cert/falsehost.go b/generate_nmc_cert/falsehost.go index d3e62ee..f48e291 100644 --- a/generate_nmc_cert/falsehost.go +++ b/generate_nmc_cert/falsehost.go @@ -14,7 +14,7 @@ // "dehydrated certificates", suitable for inclusion in a Namecoin name. // Last rebased against Go 1.8.3. -// Future rebases need to rebase both the main flow and the falseHost flow. +// Future rebases need to rebase all of the main, parent, and falseHost flows. package main @@ -88,13 +88,13 @@ func doFalseHost(parentTemplate x509.Certificate, parentPriv interface{}) { case "": //priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) log.Fatalf("Missing required --ecdsa-curve parameter") - case "P224": + case "P224": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case "P256": + case "P256": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case "P384": + case "P384": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case "P521": + case "P521": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) default: fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) diff --git a/generate_nmc_cert/main.go b/generate_nmc_cert/main.go index e63185d..0867205 100644 --- a/generate_nmc_cert/main.go +++ b/generate_nmc_cert/main.go @@ -14,7 +14,7 @@ // "dehydrated certificates", suitable for inclusion in a Namecoin name. // Last rebased against Go 1.8.3. -// Future rebases need to rebase both the main flow and the falseHost flow. +// Future rebases need to rebase all of the main, parent, and falseHost flows. package main @@ -51,6 +51,8 @@ var ( //ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") ecdsaCurve = flag.String("ecdsa-curve", "P256", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") falseHost = flag.String("false-host", "", "(Optional) Generate a false cert for this host; used to test x.509 implementations for safety regarding handling of the CA flag and KeyUsage") + useCA = flag.Bool("use-ca", false, "Use a CA instead of self-signing") + parentKey = flag.String("parent-key", "", "(Optional) Path to existing CA private key to sign with") ) func publicKey(priv interface{}) interface{} { @@ -93,13 +95,13 @@ func main() { case "": //priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) log.Fatalf("Missing required --ecdsa-curve parameter") - case "P224": + case "P224": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case "P256": + case "P256": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case "P384": + case "P384": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case "P521": + case "P521": // nolint: goconst priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) default: fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) @@ -188,7 +190,17 @@ func main() { // template.KeyUsage |= x509.KeyUsageCertSign //} - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + var parent x509.Certificate + var parentPriv interface{} + + if *useCA { + parent, parentPriv = getParent() + } else { + parent, parentPriv = template, priv + } + + //derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &parent, publicKey(priv), parentPriv) if err != nil { log.Fatalf("Failed to create certificate: %s", err) } @@ -210,6 +222,11 @@ func main() { keyOut.Close() log.Print("written key.pem\n") + if *useCA { + log.Print("SUCCESS. Place cert.pem and key.pem in your HTTPS server, and place the above JSON in the \"tls\" field for your Namecoin name.") + return + } + parsedResult, err := x509.ParseCertificate(derBytes) if err != nil { log.Fatal("failed to parse output cert: ", err) diff --git a/generate_nmc_cert/parent.go b/generate_nmc_cert/parent.go new file mode 100644 index 0000000..0fa85f3 --- /dev/null +++ b/generate_nmc_cert/parent.go @@ -0,0 +1,215 @@ +// nolint: gofmt, goimports + +// Copyright 2009 The Go Authors. All rights reserved. +// Dehydrated certificate modifications Copyright 2015-2017 Jeremy Rand. 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. + +// This code has been modified from the stock Go code to generate +// "dehydrated certificates", suitable for inclusion in a Namecoin name. + +// Last rebased against Go 1.8.3. +// Future rebases need to rebase all of the main, parent, and falseHost flows. + +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + //"crypto/rsa" + //"crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + //"flag" + "fmt" + "io/ioutil" + "log" + "math/big" + //"net" + "os" + //"strings" + "time" + + "github.com/namecoin/ncdns/x509" +) + +//var ( +// host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") +// validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") +// validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") +// isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") +// rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set") +// ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521") +//) + +//func publicKey(priv interface{}) interface{} { +// switch k := priv.(type) { +// case *rsa.PrivateKey: +// return &k.PublicKey +// case *ecdsa.PrivateKey: +// return &k.PublicKey +// default: +// return nil +// } +//} + +//func pemBlockForKey(priv interface{}) *pem.Block { +// switch k := priv.(type) { +// case *rsa.PrivateKey: +// return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} +// case *ecdsa.PrivateKey: +// b, err := x509.MarshalECPrivateKey(k) +// if err != nil { +// fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) +// os.Exit(2) +// } +// return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} +// default: +// return nil +// } +//} + +//func main() { +func getParent() (parentCert x509.Certificate, parentPriv interface{}) { +// flag.Parse() + +// if len(*host) == 0 { +// log.Fatalf("Missing required --host parameter") +// } + + var priv interface{} + var err error + switch *ecdsaCurve { + case "": + //priv, err = rsa.GenerateKey(rand.Reader, *rsaBits) + log.Fatalf("Missing required --ecdsa-curve parameter") + case "P224": // nolint: goconst + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": // nolint: goconst + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": // nolint: goconst + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": // nolint: goconst + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve) + os.Exit(1) + } + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + var privPEM []byte + if *parentKey != "" { + log.Print("Using existing CA private key") + privPEM, err = ioutil.ReadFile(*parentKey) + if err != nil { + log.Fatalf("failed to read private key: %s", err) + } + privBlock, _ := pem.Decode(privPEM) + priv, err = x509.ParseECPrivateKey(privBlock.Bytes) + if err != nil { + log.Fatalf("failed to parse private key: %s", err) + } + } + + var notBefore time.Time + if len(*validFrom) == 0 { + notBefore = time.Now() + } else { + 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) + os.Exit(1) + } + } + + notAfter := notBefore.Add(*validFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + //Organization: []string{"Acme Co"}, + CommonName: *host + " Domain CA", + SerialNumber: "Namecoin TLS Certificate", + }, + NotBefore: notBefore, + NotAfter: notAfter, + + IsCA: true, + //KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + KeyUsage: x509.KeyUsageCertSign, + //ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + + PermittedDNSDomainsCritical: true, + PermittedDNSDomains: []string{*host}, + } + + //hosts := strings.Split(*host, ",") + //for _, h := range hosts { + // if ip := net.ParseIP(h); ip != nil { + // template.IPAddresses = append(template.IPAddresses, ip) + // } else { + // template.DNSNames = append(template.DNSNames, h) + //template.DNSNames = append(template.DNSNames, *falseHost) + // } + //} + + //if *isCA { + // template.IsCA = true + // template.KeyUsage |= x509.KeyUsageCertSign + //} + + //derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + //if err != nil { + // log.Fatalf("Failed to create certificate: %s", err) + //} + + //certOut, err := os.Create("cert.pem") + //if err != nil { + // log.Fatalf("failed to open cert.pem for writing: %s", err) + //} + //pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + //certOut.Close() + //log.Print("written cert.pem\n") + + pubBytes, err := x509.MarshalPKIXPublicKey(publicKey(priv)) + if err != nil { + log.Print("failed to marshal CA public key:", err) + return + } + pubB64 := base64.StdEncoding.EncodeToString(pubBytes) + log.Printf("Your CA's \"tls\" record is: [2, 1, 0, \"%s\"]", pubB64) + + if *parentKey != "" { + return template, priv + } + + //keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + keyOut, err := os.OpenFile("caKey.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + //log.Print("failed to open key.pem for writing:", err) + log.Print("failed to open caKey.pem for writing:", err) + return + } + pem.Encode(keyOut, pemBlockForKey(priv)) + keyOut.Close() + //log.Print("written key.pem\n") + log.Print("written caKey.pem\n") + + return template, priv +}