diff --git a/ziti/cmd/ziti/cmd/edge_controller/root.go b/ziti/cmd/ziti/cmd/edge_controller/root.go index 8ebb474f1..95700487f 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/root.go +++ b/ziti/cmd/ziti/cmd/edge_controller/root.go @@ -49,6 +49,7 @@ func newCmdEdgeController(f cmdutil.Factory, out io.Writer, errOut io.Writer) *c cmd.AddCommand(newUpdateCmd(f, out, errOut)) cmd.AddCommand(newVersionCmd(f, out, errOut)) cmd.AddCommand(newPolicyAdivsorCmd(f, out, errOut)) + cmd.AddCommand(newVerifyCmd(f, out, errOut)) return cmd } diff --git a/ziti/cmd/ziti/cmd/edge_controller/verify_ca.go b/ziti/cmd/ziti/cmd/edge_controller/verify_ca.go index 33b364b14..77472e00d 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/verify_ca.go +++ b/ziti/cmd/ziti/cmd/edge_controller/verify_ca.go @@ -31,6 +31,7 @@ import ( cmdhelper "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/helpers" "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/util" nfpem "github.com/netfoundry/ziti-foundation/util/pem" + "github.com/netfoundry/ziti-foundation/util/term" "github.com/spf13/cobra" "gopkg.in/resty.v1" "io" @@ -53,6 +54,7 @@ type verifyCaOptions struct { caKeyPath string caKeyPemBytes []byte + caKeyPassword string isGenerateCert bool } @@ -70,7 +72,7 @@ func newVerifyCaCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.C } cmd := &cobra.Command{ - Use: "ca [ --cert | --cacert --cakey ]", + Use: "ca ( --cert | --cacert --cakey [--password ])", Short: "verifies a ca managed by the Ziti Edge Controller", Long: "verifies a ca managed by the Ziti Edge Controller. If --cert is supplied, it is expected that it is a certificate with the " + "common name set to the proper verificationToken value from the target CA. If not set, --cakey and --cacert can be provided to " + @@ -124,8 +126,9 @@ func newVerifyCaCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.C cmd.Flags().SetInterspersed(true) cmd.Flags().BoolVarP(&options.OutputJSONResponse, "output-json", "j", false, "Output the full JSON response from the Ziti Edge Controller") cmd.Flags().StringVarP(&options.certPath, "cert", "c", "", "The path to a cert with the CN set as the verification token and signed by the target CA") - cmd.Flags().StringVarP(&options.caKeyPath, "cacert", "a", "", "The path to the CA cert that should be used to generate and sign a verification cert") + cmd.Flags().StringVarP(&options.caCertPath, "cacert", "a", "", "The path to the CA cert that should be used to generate and sign a verification cert") cmd.Flags().StringVarP(&options.caKeyPath, "cakey", "k", "", "The path to the CA key that should be used to generate and sign a verification cert") + cmd.Flags().StringVarP(&options.caKeyPassword, "password", "p", "", "The password for the CA key if necessary") return cmd } @@ -139,7 +142,7 @@ func runValidateCa(options *verifyCaOptions) error { } if options.isGenerateCert { - jsonContainer, err := util.EdgeControllerRequest("ca/"+options.caId, options.Out, options.OutputJSONResponse, func(request *resty.Request, url string) (*resty.Response, error) { return request.Get(url) }) + jsonContainer, err := util.EdgeControllerRequest("cas/"+options.caId, options.Out, options.OutputJSONResponse, func(request *resty.Request, url string) (*resty.Response, error) { return request.Get(url) }) if err != nil { return fmt.Errorf("could not request ca [%s] (%v}", options.caId, err) @@ -158,7 +161,7 @@ func runValidateCa(options *verifyCaOptions) error { } } - return util.EdgeControllerVerify("ca", options.caId, string(options.certPemBytes), options.Out, options.OutputJSONResponse) + return util.EdgeControllerVerify("cas", options.caId, string(options.certPemBytes), options.Out, options.OutputJSONResponse) } func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer, error) { @@ -175,7 +178,6 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer if err != nil { return nil, nil, fmt.Errorf("could not parse ca cert (%v)", err) } - keyBlocks := nfpem.DecodeAll(options.caKeyPemBytes) if len(keyBlocks) == 0 { @@ -183,7 +185,27 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer } caPrivateKeyBlock := keyBlocks[0] - signerInterface, err := x509.ParsePKCS8PrivateKey(caPrivateKeyBlock.Bytes) + + if x509.IsEncryptedPEMBlock(caPrivateKeyBlock) { + if options.caKeyPassword == "" { + options.caKeyPassword, err = term.PromptPassword("enter the password for the supplied ca key file: ", false) + if err != nil { + return nil, nil, fmt.Errorf("could not retrieve password for encrypted CA key file (%v)", err) + } + } + + caPrivateKeyBlock.Bytes, err = x509.DecryptPEMBlock(caPrivateKeyBlock, []byte(options.caKeyPassword)) + if err != nil { + return nil, nil, fmt.Errorf("could not decrypt CA private key (%v)", err) + } + } + + var signerInterface crypto.Signer + if caPrivateKeyBlock.Type == "EC PRIVATE KEY" { + signerInterface, err = x509.ParseECPrivateKey(caPrivateKeyBlock.Bytes) + } else { + signerInterface, err = x509.ParsePKCS1PrivateKey(caPrivateKeyBlock.Bytes) + } if err != nil { return nil, nil, fmt.Errorf("could not parse key file (%s)", err) @@ -216,7 +238,7 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer return nil, nil, fmt.Errorf("could not generate private key for verification certificate (%v)", err) } - signedCertBytes, err := x509.CreateCertificate(rand.Reader, verificationCert, caCert, verificationKey.PublicKey, caKey) + signedCertBytes, err := x509.CreateCertificate(rand.Reader, verificationCert, caCert, verificationKey.Public(), caKey) if err != nil { return nil, nil, fmt.Errorf("could not sign verification certificate with CA (%v)", err) @@ -225,7 +247,7 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer verificationCert, _ = x509.ParseCertificate(signedCertBytes) verificationBlock := &pem.Block{ - Type: "EC CERTIFICATE", + Type: "CERTIFICATE", Bytes: verificationCert.Raw, } @@ -233,26 +255,3 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer return verificationCertPemBytes, verificationKey, nil } - -//func runCreateCa(options *createCaOptions) (err error) { -// data := gabs.New() -// setJSONValue(data, options.name, "name") -// setJSONValue(data, options.autoCaEnrollment, "isAutoCaEnrollmentEnabled") -// setJSONValue(data, options.ottCaEnrollment, "isOttCaEnrollmentEnabled") -// setJSONValue(data, options.authEnabled, "isAuthEnabled") -// setJSONValue(data, string(options.caPemBytes), "certPem") -// -// result, err := createEntityOfType("cas", data.String(), &options.commonOptions) -// -// if err != nil { -// panic(err) -// } -// -// id := result.S("data", "id").Data() -// -// if _, err = fmt.Fprintf(options.Out, "%v\n", id); err != nil { -// panic(err) -// } -// -// return err -//} diff --git a/ziti/cmd/ziti/util/rest.go b/ziti/cmd/ziti/util/rest.go index 6e71aea39..637c9661f 100644 --- a/ziti/cmd/ziti/util/rest.go +++ b/ziti/cmd/ziti/util/rest.go @@ -680,12 +680,12 @@ func EdgeControllerRequest(entityType string, out io.Writer, logJSON bool, doReq resp, err := doRequest(request, session.Host+"/"+entityType) if err != nil { - return nil, fmt.Errorf("unable to update %v instance in Ziti Edge Controller at %v. Error: %v", entityType, session.Host, err) + return nil, fmt.Errorf("unable to [%s] %v instance in Ziti Edge Controller at %v. Error: %v", request.Method, entityType, session.Host, err) } if resp.StatusCode() != http.StatusOK { - return nil, fmt.Errorf("error creating %v instance in Ziti Edge Controller at %v. Status code: %v, Server returned: %v", - entityType, session.Host, resp.Status(), resp.String()) + return nil, fmt.Errorf("error performing request [%s] %v instance in Ziti Edge Controller at %v. Status code: %v, Server returned: %v", + request.Method, entityType, session.Host, resp.Status(), resp.String()) } if logJSON {