Example #1
0
func main() {

	app := cli.NewApp()
	app.Name = "sbuca"
	app.Usage = "Simple But Useful CA"
	app.Commands = []cli.Command{

		{
			Name:  "server",
			Usage: "Run a CA server",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "dir",
					Value: ".",
					Usage: "Root directory for certificates",
				},
				cli.StringFlag{
					Name:  "address",
					Value: os.Getenv("HOST") + ":8600",
					Usage: "Token for cert signing functions",
				},
				cli.StringFlag{
					Name:  "admin-token",
					Value: "",
					Usage: "Token for administrative functions",
				},
				cli.StringFlag{
					Name:  "sign-token",
					Value: "",
					Usage: "Token for cert signing functions",
				},
			},
			Action: func(c *cli.Context) {
				config := map[string]string{
					"address":     c.String("address"),
					"root-dir":    c.String("dir"),
					"admin-token": c.String("admin-token"),
					"sign-token":  c.String("sign-token"),
				}
				server.Run(config)
			},
		},

		{
			Name:  "genkey",
			Usage: "Generate a RSA Private Key to STDOUT",
			Action: func(c *cli.Context) {
				key, err := pkix.NewKey()
				if err != nil {
					panic(err)
				}

				pem, err := key.ToPEM()
				if err != nil {
					panic(err)
				}

				fmt.Print(string(pem))
			},
		},

		{
			Name:  "gencsr",
			Usage: "Generate a Certificate Request to STDOUT",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "key",
					Usage: "RSA Private Key",
				},
			},
			Action: func(c *cli.Context) {
				keyName := c.String("key")
				if keyName == "" {
					fmt.Fprintln(os.Stderr, "[ERROR] Requere private key as parameter")
					return
				}

				key, err := pkix.NewKeyFromPrivateKeyPEMFile(keyName)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to generate CSR: "+err.Error())
					return
				}

				csr, err := pkix.NewCertificateRequest(key)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to generate CSR: "+err.Error())
					return
				}

				pem, err := csr.ToPEM()
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to generate CSR: "+err.Error())
					return
				}

				fmt.Print(string(pem))
			},
		},

		{
			Name:  "submitcsr",
			Usage: "Submit a Certificate Request to CA",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "host",
					Usage: "Host ip & port",
				},
				cli.StringFlag{
					Name:  "format",
					Value: "cert",
					Usage: "output cert or id",
				},
				cli.StringFlag{
					Name:  "token",
					Usage: "Authorization Token",
				},
			},
			Action: func(c *cli.Context) {
				host := c.String("host")
				if host == "" {
					fmt.Fprintln(os.Stderr, "[ERROR] Requere host as parameter")
					return
				}

				format := c.String("format")
				if format != "cert" && format != "id" {
					fmt.Fprintln(os.Stderr, "[ERROR] format should be 'cert' or 'id'")
					return
				}

				args := c.Args()
				if len(args) == 0 {
					fmt.Fprintln(os.Stderr, "[ERROR] Should provide csr")
					return
				}
				csrName := c.Args().First()

				csr, err := pkix.NewCertificateRequestFromPEMFile(csrName)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to parse CSR: "+err.Error())
					return
				}

				//resp, err := http.Post("http://example.com/upload", "application/json", &buf)
				//var data interface{}
				pem, err := csr.ToPEM()
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to parse CSR: "+err.Error())
					return
				}

				data := make(url.Values)
				data.Add("csr", string(pem))

				//resp, err := http.PostForm("http://"+host+"/certificates", data)
				client := &http.Client{}
				req, _ := http.NewRequest("POST", "http://"+host+"/certificates", strings.NewReader(data.Encode()))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				if c.String("token") != "" {
					req.Header.Set("X-API-KEY", c.String("token"))
				}
				resp, err := client.Do(req)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request: "+err.Error())
					return
				}
				decoder := json.NewDecoder(resp.Body)
				respData := make(map[string]map[string]interface{})
				if err := decoder.Decode(&respData); err != nil {
					panic(err)
				}

				if format == "cert" {
					fmt.Print(respData["certificate"]["crt"])
				}
				if format == "id" {
					fmt.Println(respData["certificate"]["id"])
				}
			},
		},

		{
			Name: "getcrt",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "host",
					Usage: "Host ip & port",
				},
			},
			Usage: "Get a Certificate from CA and output to STDOUT",
			Action: func(c *cli.Context) {

				host := c.String("host")
				if host == "" {
					fmt.Fprintln(os.Stderr, "[ERROR] Requere host as parameter")
					return
				}

				args := c.Args()
				if len(args) == 0 {
					fmt.Fprintln(os.Stderr, "[ERROR] Should provide id (same as serial number)")
					return
				}
				id := c.Args().First()

				resp, err := http.Get("http://" + host + "/certificates/" + id)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request: "+err.Error())
					return
				}

				decoder := json.NewDecoder(resp.Body)
				respData := make(map[string]map[string]interface{})
				if err := decoder.Decode(&respData); err != nil {
					panic(err)
				}

				fmt.Print(respData["certificate"]["crt"])

			},
		},

		{
			Name: "getcacrt",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "host",
					Usage: "Host ip & port",
				},
			},
			Usage: "Get CA's Certificate and output to STDOUT",
			Action: func(c *cli.Context) {

				host := c.String("host")
				if host == "" {
					fmt.Fprintln(os.Stderr, "[ERROR] Require host as parameter")
					return
				}

				resp, err := http.Get("http://" + host + "/ca/certificate")
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request CA cert: "+err.Error())
					os.Exit(1)
				}

				decoder := json.NewDecoder(resp.Body)
				if resp.StatusCode != 200 {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request CA cert: "+resp.Status)
					os.Exit(resp.StatusCode)
				}
				respData := make(map[string]map[string]interface{})
				if err := decoder.Decode(&respData); err != nil {
					panic(err)
				}

				fmt.Print(respData["ca"]["crt"])

			},
		},
		{
			Name: "oneshot",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "host",
					Usage: "Host ip & port",
				},
				cli.StringFlag{
					Name:  "key",
					Usage: "Path to private key file",
				},
				cli.StringFlag{
					Name:  "crt",
					Usage: "Path to public cert file",
				},
				cli.StringFlag{
					Name:  "ca",
					Usage: "Path to public ca file",
				},
				cli.StringFlag{
					Name:  "token",
					Usage: "Authorization Token",
				},
			},
			Usage: "Generate key, request, and submit to a server, all in one shot",
			Action: func(c *cli.Context) {
				keypath := c.String("key")
				if keypath == "" {
					fmt.Fprintln(os.Stderr, "Path to private key required")
					return
				}
				crtpath := c.String("crt")
				if crtpath == "" {
					fmt.Fprintln(os.Stderr, "Path to public certificate required")
					return
				}
				capath := c.String("ca")
				if capath == "" {
					fmt.Fprintln(os.Stderr, "Path to central authority required")
					return
				}
				host := c.String("host")
				if host == "" {
					fmt.Fprintln(os.Stderr, "Location of host required")
					return
				}
				keyfile, err := os.Create(keypath)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				defer keyfile.Close()

				crtfile, err := os.Create(crtpath)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				defer crtfile.Close()

				cafile, err := os.Create(capath)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				defer cafile.Close()

				// genkey
				key, err := pkix.NewKey()
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}

				pem, err := key.ToPEM()
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}

				n, err := io.WriteString(keyfile, string(pem))
				if err != nil {
					fmt.Println(n, err)
				}
				fmt.Println("Key written to", keypath)

				// gencsr
				csr, err := pkix.NewCertificateRequest(key)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to generate CSR: "+err.Error())
					return
				}
				csrpem, err := csr.ToPEM()
				// submitcsr
				data := make(url.Values)
				data.Add("csr", string(csrpem))

				//resp, err := http.PostForm("http://"+host+"/certificates", data)
				client := &http.Client{}
				req, _ := http.NewRequest("POST", "http://"+host+"/certificates", strings.NewReader(data.Encode()))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				if c.String("token") != "" {
					req.Header.Set("X-API-KEY", c.String("token"))
				}
				resp, err := client.Do(req)
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request: "+err.Error())
					return
				}
				decoder := json.NewDecoder(resp.Body)
				respData := make(map[string]map[string]interface{})
				if err := decoder.Decode(&respData); err != nil {
					panic(err)
				}

				n, err = io.WriteString(crtfile, string(respData["certificate"]["crt"].(string)))
				if err != nil {
					fmt.Println(n, err)
				}
				fmt.Println("Certificate written to", crtpath)

				// getcacrt
				resp, err = http.Get("http://" + host + "/ca/certificate")
				if err != nil {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request CA cert: "+err.Error())
					os.Exit(1)
				}

				decoder = json.NewDecoder(resp.Body)
				if resp.StatusCode != 200 {
					fmt.Fprintln(os.Stderr, "[ERROR] Failed to request CA cert: "+resp.Status)
					os.Exit(resp.StatusCode)
				}
				respData = make(map[string]map[string]interface{})
				if err := decoder.Decode(&respData); err != nil {
					panic(err)
				}

				n, err = io.WriteString(cafile, string(respData["ca"]["crt"].(string)))
				if err != nil {
					fmt.Println(n, err)
				}
				fmt.Println("Certificate Authority written to", capath)
			},
		},
	}

	app.Run(os.Args)

}
Example #2
0
func NewCA(rootDir string) (*CA, error) {

	// mkdir if needed
	if isPathNotExisted(rootDir + "/ca") {
		if err := os.MkdirAll(rootDir+"/ca", 0755); err != nil {
			return nil, err
		}
	}

	if isPathNotExisted(rootDir + "/certs") {
		if err := os.MkdirAll(rootDir+"/certs", 0755); err != nil {
			return nil, err
		}
	}

	var key *pkix.Key
	var certificate *pkix.Certificate
	var err error
	if isPathNotExisted(rootDir + "/ca/ca.key") {
		// gen priv key
		key, err = pkix.NewKey()
		if err != nil {
			return nil, err
		}
		if err := key.ToPEMFile(rootDir + "/ca/ca.key"); err != nil {
			return nil, err
		}

		// gen self-signed cert
		// should refactor, move to cert.go
		notBefore := time.Now()
		notAfter := notBefore.Add(time.Hour * 365 * 24)
		keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
		extKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
		template := &x509.Certificate{
			SerialNumber: big.NewInt(1),
			Subject: gopkix.Name{
				CommonName: "try.sbuca.com",
			},
			NotBefore:             notBefore,
			NotAfter:              notAfter,
			KeyUsage:              keyUsage,
			ExtKeyUsage:           extKeyUsage,
			BasicConstraintsValid: true,
		}

		derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.PublicKey, key.PrivateKey)
		if err != nil {
			return nil, err
		}
		certificate, err = pkix.NewCertificateFromDER(derBytes)
		if err != nil {
			return nil, err
		}
		if err := certificate.ToPEMFile(rootDir + "/ca/ca.crt"); err != nil {
			return nil, err
		}

	} else {

		certificate, err = pkix.NewCertificateFromPEMFile(rootDir + "/ca/ca.crt")
		if err != nil {
			return nil, err
		}
		key, err = pkix.NewKeyFromPrivateKeyPEMFile(rootDir + "/ca/ca.key")
		if err != nil {
			return nil, err
		}

	}

	if isPathNotExisted(rootDir + "/ca/ca.srl") {
		ioutil.WriteFile(rootDir+"/ca/ca.srl", []byte("2"), 0644)
	}

	certStore := NewCertStore(rootDir + "/certs")
	newCA := &CA{
		RootDir:     rootDir,
		CertStore:   certStore,
		Certificate: certificate,
		Key:         key,
	}

	return newCA, nil
}