From 8f6b4d913b5fdf9e5a58b7a08ad1a036a74265fd Mon Sep 17 00:00:00 2001 From: Andrew Martinez Date: Thu, 27 Jul 2023 12:21:14 -0400 Subject: [PATCH 1/3] fixed #65 adds EC generation support to the PKI sub command --- ziti/cmd/lets_encrypt/accounts_storage.go | 2 +- ziti/cmd/pki.go | 3 +- ziti/cmd/pki_create.go | 193 ++++++++++++++-------- ziti/cmd/pki_create_ca.go | 22 ++- ziti/cmd/pki_create_client.go | 22 ++- ziti/cmd/pki_create_csr.go | 19 +-- ziti/cmd/pki_create_intermediate.go | 26 ++- ziti/cmd/pki_create_key.go | 23 ++- ziti/cmd/pki_create_server.go | 14 +- ziti/pki/certificate/certificate.go | 9 +- ziti/pki/pki/pki.go | 97 +++++++++-- ziti/pki/pki/template.go | 6 +- ziti/pki/store/local.go | 32 +++- ziti/pki/store/store.go | 2 +- 14 files changed, 323 insertions(+), 147 deletions(-) diff --git a/ziti/cmd/lets_encrypt/accounts_storage.go b/ziti/cmd/lets_encrypt/accounts_storage.go index 45faf02c4..1eb73690e 100644 --- a/ziti/cmd/lets_encrypt/accounts_storage.go +++ b/ziti/cmd/lets_encrypt/accounts_storage.go @@ -183,7 +183,7 @@ func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.Priva privateKey, err := loadPrivateKey(accKeyPath) if err != nil { - log.Fatalf("Could not load RSA private key from file %s: %v", accKeyPath, err) + log.Fatalf("Could not load private key from file %s: %v", accKeyPath, err) } return privateKey diff --git a/ziti/cmd/pki.go b/ziti/cmd/pki.go index e1991f270..28c5a1a90 100644 --- a/ziti/cmd/pki.go +++ b/ziti/cmd/pki.go @@ -43,7 +43,7 @@ type PKIFlags struct { CAName string CommonName string CAExpire int - CAMaxpath int + CAMaxPath int CAPrivateKeySize int IntermediateFile string IntermediateName string @@ -61,6 +61,7 @@ type PKIFlags struct { PKI *pki.ZitiPKI SpiffeID string AllowOverwrite bool + EcCurve string } var ( diff --git a/ziti/cmd/pki_create.go b/ziti/cmd/pki_create.go index 4ad54c0d4..9d75dca8e 100644 --- a/ziti/cmd/pki_create.go +++ b/ziti/cmd/pki_create.go @@ -17,16 +17,18 @@ package cmd import ( + "crypto/elliptic" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" + "github.com/openziti/ziti/ziti/pki/pki" "github.com/openziti/ziti/ziti/util" "io" - "io/ioutil" "net" + "os" "strings" "sync" "time" @@ -78,32 +80,32 @@ func NewCmdPKICreate(out io.Writer, errOut io.Writer) *cobra.Command { func (options *PKICreateOptions) addPKICreateFlags(cmd *cobra.Command) { viperLock.Lock() cmd.PersistentFlags().StringVarP(&options.Flags.PKIRoot, "pki-root", "", "", "Directory in which to store CA") - cmd.MarkFlagRequired("pki-root") - viper.BindPFlag("pki_root", cmd.PersistentFlags().Lookup("pki-root")) + _ = cmd.MarkFlagRequired("pki-root") + _ = viper.BindPFlag("pki_root", cmd.PersistentFlags().Lookup("pki-root")) cmd.PersistentFlags().StringVarP(&options.Flags.PKIOrganization, "pki-organization", "", "NetFoundry", "Organization") - cmd.MarkFlagRequired("pki-organization") - viper.BindPFlag("pki-organization", cmd.PersistentFlags().Lookup("pki-organization")) + _ = cmd.MarkFlagRequired("pki-organization") + _ = viper.BindPFlag("pki-organization", cmd.PersistentFlags().Lookup("pki-organization")) cmd.PersistentFlags().StringVarP(&options.Flags.PKIOrganizationalUnit, "pki-organizational-unit", "", "ADV-DEV", "Organization unit") - cmd.MarkFlagRequired("pki-organizational-unit") - viper.BindPFlag("pki-organizational-unit", cmd.PersistentFlags().Lookup("pki-organizational-unit")) + _ = cmd.MarkFlagRequired("pki-organizational-unit") + _ = viper.BindPFlag("pki-organizational-unit", cmd.PersistentFlags().Lookup("pki-organizational-unit")) cmd.PersistentFlags().StringVarP(&options.Flags.PKICountry, "pki-country", "", "US", "Country") - cmd.MarkFlagRequired("pki-country") - viper.BindPFlag("pki-country", cmd.PersistentFlags().Lookup("pki-country")) + _ = cmd.MarkFlagRequired("pki-country") + _ = viper.BindPFlag("pki-country", cmd.PersistentFlags().Lookup("pki-country")) cmd.PersistentFlags().StringVarP(&options.Flags.PKILocality, "pki-locality", "", "Charlotte", "Locality/Location") - cmd.MarkFlagRequired("pki-locality") - viper.BindPFlag("pki-locality", cmd.PersistentFlags().Lookup("pki-locality")) + _ = cmd.MarkFlagRequired("pki-locality") + _ = viper.BindPFlag("pki-locality", cmd.PersistentFlags().Lookup("pki-locality")) // cmd.PersistentFlags().StringVarP(&options.Flags.PKILocality, "pki-location", "", "Charlotte", "Location/Locality") // cmd.MarkFlagRequired("pki-location") // viper.BindPFlag("pki-location", cmd.PersistentFlags().Lookup("pki-location")) cmd.PersistentFlags().StringVarP(&options.Flags.PKIProvince, "pki-province", "", "NC", "Province/State") - cmd.MarkFlagRequired("pki-province") - viper.BindPFlag("pki-province", cmd.PersistentFlags().Lookup("pki-province")) + _ = cmd.MarkFlagRequired("pki-province") + _ = viper.BindPFlag("pki-province", cmd.PersistentFlags().Lookup("pki-province")) // cmd.PersistentFlags().StringVarP(&options.Flags.PKIProvince, "pki-state", "", "NC", "State/Province") // cmd.MarkFlagRequired("pki-state") @@ -118,111 +120,111 @@ func (o *PKICreateOptions) Run() error { // ObtainPKIRoot returns the value for pki-root func (o *PKICreateOptions) ObtainPKIRoot() (string, error) { - pkiroot := o.Flags.PKIRoot - if pkiroot == "" { - pkiroot = viper.GetString("pki-root") - if pkiroot == "" { + pkiRoot := o.Flags.PKIRoot + if pkiRoot == "" { + pkiRoot = viper.GetString("pki-root") + if pkiRoot == "" { pkiRootDir, err := util.PKIRootDir() if err != nil { return "", err } - pkiroot, err = util.PickValue("Required flag 'pki-root' not specified; Enter PKI Root now:", pkiRootDir, true) + pkiRoot, err = util.PickValue("Required flag 'pki-root' not specified; Enter PKI Root now:", pkiRootDir, true) if err != nil { return "", err } } } - return pkiroot, nil + return pkiRoot, nil } // ObtainCAFile returns the value for ca-file func (o *PKICreateOptions) ObtainCAFile() (string, error) { - cafile := o.Flags.CAFile - if cafile == "" { - cafile = viper.GetString("ca-file") - if cafile == "" { + caFile := o.Flags.CAFile + if caFile == "" { + caFile = viper.GetString("ca-file") + if caFile == "" { var err error - cafile, err = util.PickValue("Required flag 'ca-file' not specified; Enter CA name now:", "ca", true) + caFile, err = util.PickValue("Required flag 'ca-file' not specified; Enter CA name now:", "ca", true) if err != nil { return "", err } } } - return cafile, nil + return caFile, nil } // ObtainIntermediateCAFile returns the value for intermediate-file func (o *PKICreateOptions) ObtainIntermediateCAFile() (string, error) { - intermediatefile := o.Flags.IntermediateFile - if intermediatefile == "" { - intermediatefile = viper.GetString("intermediate-file") - if intermediatefile == "" { + intermediateFile := o.Flags.IntermediateFile + if intermediateFile == "" { + intermediateFile = viper.GetString("intermediate-file") + if intermediateFile == "" { var err error - intermediatefile, err = util.PickValue("Required flag 'intermediate-file' not specified; Enter Intermediate CA name now:", "intermediate", true) + intermediateFile, err = util.PickValue("Required flag 'intermediate-file' not specified; Enter Intermediate CA name now:", "intermediate", true) if err != nil { return "", err } } } - return intermediatefile, nil + return intermediateFile, nil } // ObtainIntermediateCSRFile returns the value for intermediate-file func (o *PKICreateOptions) ObtainIntermediateCSRFile() (string, error) { - intermediatecsrfile := viper.GetString("intermediate-csr-file") - if intermediatecsrfile == "" { + intermediateCsrFile := viper.GetString("intermediate-csr-file") + if intermediateCsrFile == "" { var err error - intermediatecsrfile, err = util.PickValue("Required flag 'intermediate--csr-file' not specified; Enter Intermediate CSR file name now:", "intermediate-csr", true) + intermediateCsrFile, err = util.PickValue("Required flag 'intermediate--csr-file' not specified; Enter Intermediate CSR file name now:", "intermediate-csr", true) if err != nil { return "", err } } - return intermediatecsrfile, nil + return intermediateCsrFile, nil } // ObtainCSRFile returns the value for csr-file func (o *PKICreateOptions) ObtainCSRFile() (string, error) { - csrfile := viper.GetString("csr-file") - if csrfile == "" { + csrFile := viper.GetString("csr-file") + if csrFile == "" { var err error - csrfile, err = util.PickValue("Required flag 'csr-file' not specified; Enter CSR name now:", "csr", true) + csrFile, err = util.PickValue("Required flag 'csr-file' not specified; Enter CSR name now:", "csr", true) if err != nil { return "", err } } - return csrfile, nil + return csrFile, nil } // ObtainServerCertFile returns the value for server-file func (o *PKICreateOptions) ObtainServerCertFile() (string, error) { - serverfile := o.Flags.ServerFile - if serverfile == "" { - serverfile = viper.GetString("server-file") - if serverfile == "" { + serverFile := o.Flags.ServerFile + if serverFile == "" { + serverFile = viper.GetString("server-file") + if serverFile == "" { var err error - serverfile, err = util.PickValue("Required flag 'server-file' not specified; Enter name now:", "server", true) + serverFile, err = util.PickValue("Required flag 'server-file' not specified; Enter name now:", "server", true) if err != nil { return "", err } } } - return serverfile, nil + return serverFile, nil } // ObtainClientCertFile returns the value for client-file func (o *PKICreateOptions) ObtainClientCertFile() (string, error) { - clientfile := o.Flags.ClientFile - if clientfile == "" { - clientfile = viper.GetString("client-file") - if clientfile == "" { + clientFile := o.Flags.ClientFile + if clientFile == "" { + clientFile = viper.GetString("client-file") + if clientFile == "" { var err error - clientfile, err = util.PickValue("Required flag 'client-file' not specified; Enter name now:", "client", true) + clientFile, err = util.PickValue("Required flag 'client-file' not specified; Enter name now:", "client", true) if err != nil { return "", err } } } - return clientfile, nil + return clientFile, nil } // ObtainKeyFile returns the value for key-file @@ -244,13 +246,13 @@ func (o *PKICreateOptions) ObtainKeyFile(required bool) (string, error) { } // ObtainCAName returns the value for ca-name -func (o *PKICreateOptions) ObtainCAName(pkiroot string) (string, error) { - caname := o.Flags.CAName - if caname == "" { - caname = viper.GetString("ca-name") - if caname == "" { +func (o *PKICreateOptions) ObtainCAName(pkiRoot string) (string, error) { + caName := o.Flags.CAName + if caName == "" { + caName = viper.GetString("ca-name") + if caName == "" { var err error - files, err := ioutil.ReadDir(pkiroot) + files, err := os.ReadDir(pkiRoot) if err != nil { return "", err } @@ -262,14 +264,14 @@ func (o *PKICreateOptions) ObtainCAName(pkiroot string) (string, error) { } } } - caname, err = util.PickName(names, "Required flag 'ca-name' not specified; choose from below (dirs seen in your ZITI_PKI_ROOT):") + caName, err = util.PickName(names, "Required flag 'ca-name' not specified; choose from below (dirs seen in your ZITI_PKI_ROOT):") if err != nil { return "", err } } } - fmt.Println("Using CA name: ", caname) - return caname, nil + fmt.Println("Using CA name: ", caName) + return caName, nil } // ObtainCommonName returns the value for CN @@ -289,9 +291,9 @@ func (o *PKICreateOptions) ObtainCommonName() (string, error) { } // ObtainFileName returns the value for the 'name' used in the PKI request -func (o *PKICreateOptions) ObtainFileName(cafile string, commonName string) string { +func (o *PKICreateOptions) ObtainFileName(caFile string, commonName string) string { var filename string - if filename = cafile; len(cafile) == 0 { + if filename = caFile; len(caFile) == 0 { filename = strings.Replace(commonName, " ", "_", -1) filename = strings.Replace(filename, "*", "wildcard", -1) } @@ -321,18 +323,18 @@ func (o *PKICreateOptions) ObtainPKIRequestTemplate(commonName string) *x509.Cer template := &x509.Certificate{ Subject: subject, NotAfter: time.Now().AddDate(0, 0, o.Flags.CAExpire), - MaxPathLen: o.Flags.CAMaxpath, + MaxPathLen: o.Flags.CAMaxPath, } return template } // ObtainKeyName returns the private key from the key-file -func (o *PKICreateOptions) ObtainKeyName(pkiroot string) (string, error) { - keyname := viper.GetString("key-name") - if keyname == "" { +func (o *PKICreateOptions) ObtainKeyName(pkiRoot string) (string, error) { + keyName := viper.GetString("key-name") + if keyName == "" { var err error - files, err := ioutil.ReadDir(pkiroot) + files, err := os.ReadDir(pkiRoot) if err != nil { return "", err } @@ -342,13 +344,13 @@ func (o *PKICreateOptions) ObtainKeyName(pkiroot string) (string, error) { names = append(names, f.Name()) } } - keyname, err = util.PickName(names, "Required flag 'key-name' not specified; choose from below (dirs seen in your ZITI_PKI_ROOT):") + keyName, err = util.PickName(names, "Required flag 'key-name' not specified; choose from below (dirs seen in your ZITI_PKI_ROOT):") if err != nil { return "", err } } - return keyname, nil + return keyName, nil } // ObtainPKICSRRequestTemplate returns the CSR 'template' used in the PKI request @@ -379,8 +381,7 @@ func (o *PKICreateOptions) ObtainPKICSRRequestTemplate(commonName string) *x509. val, _ := asn1.Marshal(basicConstraints{true, 0}) csrTemplate := &x509.CertificateRequest{ - Subject: subject, - SignatureAlgorithm: x509.SHA512WithRSA, + Subject: subject, ExtraExtensions: []pkix.Extension{ { Id: asn1.ObjectIdentifier{2, 5, 29, 19}, @@ -393,7 +394,7 @@ func (o *PKICreateOptions) ObtainPKICSRRequestTemplate(commonName string) *x509. return csrTemplate } -// ObtainIPsAndDNSNames returns the IP addrs and/or DNS names used in the PKI request template +// ObtainIPsAndDNSNames returns the IP address and/or DNS names used in the PKI request template func (o *PKICreateOptions) ObtainIPsAndDNSNames() ([]net.IP, []string, error) { if (len(o.Flags.IP) == 0) && (len(o.Flags.DNSName) == 0) { @@ -409,3 +410,51 @@ func (o *PKICreateOptions) ObtainIPsAndDNSNames() ([]net.IP, []string, error) { return IPs, o.Flags.DNSName, nil } + +// ObtainPrivateKeyOptions returns the private key options necessary to generate a private key +func (o *PKICreateOptions) ObtainPrivateKeyOptions() (pki.PrivateKeyOptions, error) { + isEc := o.Flags.EcCurve != "" + + if isEc { + return o.obtainEcOptions() + } + + return o.obtainRsaOptions() +} + +func (o *PKICreateOptions) obtainEcOptions() (pki.PrivateKeyOptions, error) { + target := strings.Replace(strings.ToLower(o.Flags.EcCurve), "-", "", -1) + + validCurves := []elliptic.Curve{ + elliptic.P224(), + elliptic.P256(), + elliptic.P384(), + elliptic.P521(), + } + for _, validCurve := range validCurves { + curCurveName := strings.Replace(strings.ToLower(validCurve.Params().Name), "-", "", -1) + + if curCurveName == target { + return &pki.EcPrivateKeyOptions{ + Curve: validCurve, + }, nil + } + } + + validCurveString := "" + + for _, validCurve := range validCurves { + if validCurveString != "" { + validCurveString = validCurveString + ", " + } + + validCurveString = validCurveString + strings.Replace(validCurve.Params().Name, "-", "", -1) + } + return nil, fmt.Errorf("unknown curve name '%s', valid curves (case and dash insensitive) %s", o.Flags.EcCurve, validCurveString) +} + +func (o *PKICreateOptions) obtainRsaOptions() (pki.PrivateKeyOptions, error) { + return &pki.RsaPrivateKeyOptions{ + Size: o.Flags.CAPrivateKeySize, + }, nil +} diff --git a/ziti/cmd/pki_create_ca.go b/ziti/cmd/pki_create_ca.go index 9f1f4e187..581fdb26d 100644 --- a/ziti/cmd/pki_create_ca.go +++ b/ziti/cmd/pki_create_ca.go @@ -17,6 +17,7 @@ package cmd import ( + "fmt" "github.com/openziti/fabric/controller/idgen" cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/internal/log" @@ -70,23 +71,24 @@ func (o *PKICreateCAOptions) addPKICreateCAFlags(cmd *cobra.Command) { cmd.Flags().StringVarP(&o.Flags.CAFile, "ca-file", "", "", "Dir/File name (within PKI_ROOT) in which to store new CA") cmd.Flags().StringVarP(&o.Flags.CAName, FlagCaName, "", "NetFoundry Inc. Certificate Authority", "Name of CA") cmd.Flags().IntVarP(&o.Flags.CAExpire, "expire-limit", "", 3650, "Expiration limit in days") - cmd.Flags().IntVarP(&o.Flags.CAMaxpath, "max-path-len", "", -1, "Intermediate maximum path length") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAMaxPath, "max-path-len", "", -1, "Intermediate maximum path length") + cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set") + cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521") cmd.Flags().StringVar(&o.Flags.SpiffeID, "trust-domain", "", "An optional spiffe trust domain. spiffe:// will be automatically prefixed, if not provided") } // Run implements this command func (o *PKICreateCAOptions) Run() error { - pkiroot, err := o.ObtainPKIRoot() + pkiRoot, err := o.ObtainPKIRoot() if err != nil { return err } o.Flags.PKI = &pki.ZitiPKI{Store: &store.Local{}} local := o.Flags.PKI.Store.(*store.Local) - local.Root = pkiroot + local.Root = pkiRoot - cafile, err := o.ObtainCAFile() + caFile, err := o.ObtainCAFile() if err != nil { return err } @@ -98,7 +100,7 @@ func (o *PKICreateCAOptions) Run() error { commonName := o.Flags.CAName - filename := o.ObtainFileName(cafile, commonName) + filename := o.ObtainFileName(caFile, commonName) template := o.ObtainPKIRequestTemplate(commonName) template.IsCA = true @@ -114,13 +116,19 @@ func (o *PKICreateCAOptions) Run() error { template.URIs = append(template.URIs, spiffeId) } + privateKeyOptions, err := o.ObtainPrivateKeyOptions() + + if err != nil { + return fmt.Errorf("could not resolve private key options: %w", err) + } + var signer *certificate.Bundle req := &pki.Request{ Name: filename, Template: template, IsClientCertificate: false, - PrivateKeySize: o.Flags.CAPrivateKeySize, + PrivateKeyOptions: privateKeyOptions, } if err = o.Flags.PKI.Sign(signer, req); err != nil { diff --git a/ziti/cmd/pki_create_client.go b/ziti/cmd/pki_create_client.go index 4d4f70aaf..56162265a 100644 --- a/ziti/cmd/pki_create_client.go +++ b/ziti/cmd/pki_create_client.go @@ -17,6 +17,7 @@ package cmd import ( + "fmt" "github.com/openziti/fabric/controller/idgen" cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/internal/log" @@ -73,21 +74,22 @@ func (o *PKICreateClientOptions) addPKICreateClientFlags(cmd *cobra.Command) { cmd.Flags().StringVarP(&o.Flags.ClientName, FlagCaClientName, "", "NetFoundry Inc. Client", "Common Name (CN) to use for new Client certificate") cmd.Flags().StringSliceVar(&o.Flags.Email, "email", []string{}, "Email addr(s) to add to Subject Alternate Name (SAN) for new Client certificate") cmd.Flags().IntVarP(&o.Flags.CAExpire, "expire-limit", "", 365, "Expiration limit in days") - cmd.Flags().IntVarP(&o.Flags.CAMaxpath, "max-path-len", "", -1, "Intermediate maximum path length") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 2048, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAMaxPath, "max-path-len", "", -1, "Intermediate maximum path length") + cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set") + cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521") cmd.Flags().StringVar(&o.Flags.SpiffeID, "spiffe-id", "", "Optionally provide the path portion of a SPIFFE id. The trust domain will be taken from the signing certificate.") } // Run implements this command func (o *PKICreateClientOptions) Run() error { - pkiroot, err := o.ObtainPKIRoot() + pkiRoot, err := o.ObtainPKIRoot() if err != nil { return err } o.Flags.PKI = &pki.ZitiPKI{Store: &store.Local{}} local := o.Flags.PKI.Store.(*store.Local) - local.Root = pkiroot + local.Root = pkiRoot if !o.Cmd.Flags().Changed(FlagCaClientName) { o.Flags.ClientName = o.Flags.ClientName + " " + idgen.New() @@ -108,7 +110,7 @@ func (o *PKICreateClientOptions) Run() error { var signer *certificate.Bundle - caname, err := o.ObtainCAName(pkiroot) + caName, err := o.ObtainCAName(pkiRoot) if err != nil { return err } @@ -118,7 +120,7 @@ func (o *PKICreateClientOptions) Run() error { return err } - signer, err = o.Flags.PKI.GetCA(caname) + signer, err = o.Flags.PKI.GetCA(caName) if err != nil { return errors.Wrap(err, "cannot locate signer") } @@ -143,12 +145,18 @@ func (o *PKICreateClientOptions) Run() error { template.URIs = append(template.URIs, &spiffId) } + privateKeyOptions, err := o.ObtainPrivateKeyOptions() + + if err != nil { + return fmt.Errorf("could not resolve private key options: %w", err) + } + req := &pki.Request{ Name: filename, KeyName: keyFile, Template: template, IsClientCertificate: true, - PrivateKeySize: o.Flags.CAPrivateKeySize, + PrivateKeyOptions: privateKeyOptions, } if err := o.Flags.PKI.Sign(signer, req); err != nil { diff --git a/ziti/cmd/pki_create_csr.go b/ziti/cmd/pki_create_csr.go index e45fac863..99cfc7523 100644 --- a/ziti/cmd/pki_create_csr.go +++ b/ziti/cmd/pki_create_csr.go @@ -64,28 +64,27 @@ func (o *PKICreateCSROptions) addPKICreateCSRFlags(cmd *cobra.Command) { cmd.Flags().StringVarP(&o.Flags.CSRName, "csr-name", "", "NetFoundry Inc. CSR", "Name of CSR") cmd.Flags().StringVarP(&o.Flags.KeyName, "key-name", "", "", "Name of file that contains private key for CSR") cmd.Flags().IntVarP(&o.Flags.CAExpire, "expire-limit", "", 365, "Expiration limit in days") - cmd.Flags().IntVarP(&o.Flags.CAMaxpath, "max-path-len", "", -1, "Intermediate maximum path length") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAMaxPath, "max-path-len", "", -1, "Intermediate maximum path length") } // Run implements this command func (o *PKICreateCSROptions) Run() error { - pkiroot, err := o.ObtainPKIRoot() + pkiRoot, err := o.ObtainPKIRoot() if err != nil { return fmt.Errorf("%s", err) } o.Flags.PKI = &pki.ZitiPKI{Store: &store.Local{}} local := o.Flags.PKI.Store.(*store.Local) - local.Root = pkiroot + local.Root = pkiRoot - csrfile, err := o.ObtainCSRFile() + csrFile, err := o.ObtainCSRFile() if err != nil { return fmt.Errorf("%s", err) } - keyname, err := o.ObtainKeyName(pkiroot) + keyName, err := o.ObtainKeyName(pkiRoot) if err != nil { return fmt.Errorf("%s", err) } @@ -93,13 +92,13 @@ func (o *PKICreateCSROptions) Run() error { commonName := o.Flags.CSRName template := o.ObtainPKICSRRequestTemplate(commonName) - key, err := o.Flags.PKI.GetPrivateKey(keyname, keyname) + key, err := o.Flags.PKI.GetPrivateKey(keyName, keyName) if err != nil { - return fmt.Errorf("Cannot locate private key: %v", err) + return fmt.Errorf("cannot locate private key: %v", err) } - if err := o.Flags.PKI.CSR(keyname, csrfile, *template, key); err != nil { - return fmt.Errorf("Cannot create CSR: %v", err) + if err := o.Flags.PKI.CSR(keyName, csrFile, *template, key); err != nil { + return fmt.Errorf("cannot create CSR: %v", err) } log.Infoln("Success") diff --git a/ziti/cmd/pki_create_intermediate.go b/ziti/cmd/pki_create_intermediate.go index 0b1648204..2f80ad773 100644 --- a/ziti/cmd/pki_create_intermediate.go +++ b/ziti/cmd/pki_create_intermediate.go @@ -17,6 +17,7 @@ package cmd import ( + "fmt" "github.com/openziti/fabric/controller/idgen" cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/internal/log" @@ -70,22 +71,23 @@ func (o *PKICreateIntermediateOptions) addPKICreateIntermediateFlags(cmd *cobra. cmd.Flags().StringVarP(&o.Flags.IntermediateFile, "intermediate-file", "", "intermediate", "Dir/File name (within PKI_ROOT) in which to store new Intermediate CA") cmd.Flags().StringVarP(&o.Flags.IntermediateName, FlagCaIntermediateName, "", "NetFoundry Inc. Intermediate CA", "Common Name (CN) to use for new Intermediate CA") cmd.Flags().IntVarP(&o.Flags.CAExpire, "expire-limit", "", 3650, "Expiration limit in days") - cmd.Flags().IntVarP(&o.Flags.CAMaxpath, "max-path-len", "", 0, "Intermediate maximum path length") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAMaxPath, "max-path-len", "", 0, "Intermediate maximum path length") + cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set") + cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521") } // Run implements this command func (o *PKICreateIntermediateOptions) Run() error { - pkiroot, err := o.ObtainPKIRoot() + pkiRoot, err := o.ObtainPKIRoot() if err != nil { return err } o.Flags.PKI = &pki.ZitiPKI{Store: &store.Local{}} local := o.Flags.PKI.Store.(*store.Local) - local.Root = pkiroot + local.Root = pkiRoot - intermediatefile, err := o.ObtainIntermediateCAFile() + intermediateFile, err := o.ObtainIntermediateCAFile() if err != nil { return err } @@ -96,19 +98,19 @@ func (o *PKICreateIntermediateOptions) Run() error { commonName := o.Flags.IntermediateName - filename := o.ObtainFileName(intermediatefile, commonName) + filename := o.ObtainFileName(intermediateFile, commonName) template := o.ObtainPKIRequestTemplate(commonName) template.IsCA = true var signer *certificate.Bundle - caname, err := o.ObtainCAName(pkiroot) + caName, err := o.ObtainCAName(pkiRoot) if err != nil { return err } - signer, err = o.Flags.PKI.GetCA(caname) + signer, err = o.Flags.PKI.GetCA(caName) if err != nil { return errors.Wrap(err, "cannot locate signer") } @@ -119,11 +121,17 @@ func (o *PKICreateIntermediateOptions) Run() error { } } + privateKeyOptions, err := o.ObtainPrivateKeyOptions() + + if err != nil { + return fmt.Errorf("could not resolve private key options: %w", err) + } + req := &pki.Request{ Name: filename, Template: template, IsClientCertificate: false, - PrivateKeySize: o.Flags.CAPrivateKeySize, + PrivateKeyOptions: privateKeyOptions, } if err := o.Flags.PKI.Sign(signer, req); err != nil { diff --git a/ziti/cmd/pki_create_key.go b/ziti/cmd/pki_create_key.go index 07954d88a..e2ff8b018 100644 --- a/ziti/cmd/pki_create_key.go +++ b/ziti/cmd/pki_create_key.go @@ -65,27 +65,28 @@ func (o *PKICreateKeyOptions) addPKICreateKeyFlags(cmd *cobra.Command) { cmd.Flags().StringVarP(&o.Flags.PKIRoot, "pki-root", "", "", "Directory in which PKI resides") cmd.Flags().StringVarP(&o.Flags.CAName, "ca-name", "", "intermediate", "Name of Intermediate CA (within PKI_ROOT) to use to sign the new Client certificate") cmd.Flags().StringVarP(&o.Flags.KeyFile, "key-file", "", "key", "Name of file (under chosen CA) in which to store new private key") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set") + cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521") } // Run implements this command func (o *PKICreateKeyOptions) Run() error { - pkiroot, err := o.ObtainPKIRoot() + pkiRoot, err := o.ObtainPKIRoot() if err != nil { return fmt.Errorf("%s", err) } o.Flags.PKI = &pki.ZitiPKI{Store: &store.Local{}} local := o.Flags.PKI.Store.(*store.Local) - local.Root = pkiroot + local.Root = pkiRoot keyFile, err := o.ObtainKeyFile(true) if err != nil { return fmt.Errorf("%s", err) } - caname, err := o.ObtainCAName(pkiroot) + caName, err := o.ObtainCAName(pkiRoot) if err != nil { return fmt.Errorf("%s", err) } @@ -93,20 +94,26 @@ func (o *PKICreateKeyOptions) Run() error { template := o.ObtainPKIRequestTemplate("") var signer *certificate.Bundle - signer, err = o.Flags.PKI.GetCA(caname) + signer, err = o.Flags.PKI.GetCA(caName) if err != nil { - return fmt.Errorf("Cannot locate signer: %v", err) + return fmt.Errorf("cannot locate signer: %v", err) + } + + privateKeyOptions, err := o.ObtainPrivateKeyOptions() + + if err != nil { + return fmt.Errorf("could not resolve private key options: %w", err) } req := &pki.Request{ KeyName: keyFile, Template: template, IsClientCertificate: false, - PrivateKeySize: o.Flags.CAPrivateKeySize, + PrivateKeyOptions: privateKeyOptions, } if err := o.Flags.PKI.GeneratePrivateKey(signer, req); err != nil { - return fmt.Errorf("Cannot Generate Private key: %v", err) + return fmt.Errorf("cannot Generate Private key: %v", err) } log.Infoln("Success") diff --git a/ziti/cmd/pki_create_server.go b/ziti/cmd/pki_create_server.go index 6cc578e3d..98deef32d 100644 --- a/ziti/cmd/pki_create_server.go +++ b/ziti/cmd/pki_create_server.go @@ -17,6 +17,7 @@ package cmd import ( + "fmt" "github.com/openziti/fabric/controller/idgen" cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/internal/log" @@ -74,8 +75,9 @@ func (o *PKICreateServerOptions) addPKICreateServerFlags(cmd *cobra.Command) { cmd.Flags().StringSliceVar(&o.Flags.DNSName, "dns", []string{}, "DNS name(s) to add to Subject Alternate Name (SAN) for new Server certificate") cmd.Flags().StringSliceVar(&o.Flags.IP, "ip", []string{}, "IP addr(s) to add to Subject Alternate Name (SAN) for new Server certificate") cmd.Flags().IntVarP(&o.Flags.CAExpire, "expire-limit", "", 365, "Expiration limit in days") - cmd.Flags().IntVarP(&o.Flags.CAMaxpath, "max-path-len", "", -1, "Intermediate maximum path length") - cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the private key") + cmd.Flags().IntVarP(&o.Flags.CAMaxPath, "max-path-len", "", -1, "Intermediate maximum path length") + cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set") + cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521") cmd.Flags().StringVar(&o.Flags.SpiffeID, "spiffe-id", "", "Optionally provide the path portion of a SPIFFE id. The trust domain will be taken from the signing certificate.") cmd.Flags().BoolVar(&o.Flags.AllowOverwrite, "allow-overwrite", false, "Allow overwrite existing certs") } @@ -151,12 +153,18 @@ func (o *PKICreateServerOptions) Run() error { template.URIs = append(template.URIs, &spiffId) } + privateKeyOptions, err := o.ObtainPrivateKeyOptions() + + if err != nil { + return fmt.Errorf("could not resolve private key options: %w", err) + } + req := &pki.Request{ Name: filename, KeyName: keyFile, Template: template, IsClientCertificate: false, - PrivateKeySize: o.Flags.CAPrivateKeySize, + PrivateKeyOptions: privateKeyOptions, AllowOverwrite: o.Flags.AllowOverwrite, } diff --git a/ziti/pki/certificate/certificate.go b/ziti/pki/certificate/certificate.go index 9b229f423..6852ca9ab 100644 --- a/ziti/pki/certificate/certificate.go +++ b/ziti/pki/certificate/certificate.go @@ -17,7 +17,7 @@ package certificate import ( - "crypto/rsa" + "crypto" "crypto/x509" "fmt" ) @@ -25,19 +25,20 @@ import ( // Bundle represents a pair of private key and certificate. type Bundle struct { Name string - Key *rsa.PrivateKey + Key crypto.PrivateKey Cert *x509.Certificate } // Raw returns the raw bytes for the private key and certificate. func (b *Bundle) Raw() ([]byte, []byte) { - return x509.MarshalPKCS1PrivateKey(b.Key), b.Cert.Raw + keyBytes, _ := x509.MarshalPKCS8PrivateKey(b.Key) + return keyBytes, b.Cert.Raw } // RawToBundle creates a bundle from the name and bytes given for a private key // and a certificate. func RawToBundle(name string, key []byte, cert []byte) (*Bundle, error) { - k, err := x509.ParsePKCS1PrivateKey(key) + k, err := x509.ParsePKCS8PrivateKey(key) if err != nil { return nil, fmt.Errorf("failed parsing private key: %v", err) } diff --git a/ziti/pki/pki/pki.go b/ziti/pki/pki/pki.go index e33224f76..e13194ae3 100644 --- a/ziti/pki/pki/pki.go +++ b/ziti/pki/pki/pki.go @@ -20,6 +20,7 @@ package pki import ( "crypto" "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -42,13 +43,40 @@ var ( ErrMaxPathLenReached = errors.New("max path len reached") ) +type PrivateKeyOptions interface { + GenerateKey() (crypto.PrivateKey, error) +} + +var _ PrivateKeyOptions = &RsaPrivateKeyOptions{} + +type RsaPrivateKeyOptions struct { + Size int +} + +func (r *RsaPrivateKeyOptions) GenerateKey() (crypto.PrivateKey, error) { + if r.Size == 0 { + r.Size = defaultPrivateKeySize + } + return rsa.GenerateKey(rand.Reader, r.Size) +} + +var _ PrivateKeyOptions = &EcPrivateKeyOptions{} + +type EcPrivateKeyOptions struct { + Curve elliptic.Curve +} + +func (e *EcPrivateKeyOptions) GenerateKey() (crypto.PrivateKey, error) { + return ecdsa.GenerateKey(e.Curve, rand.Reader) +} + // Request is a struct for providing configuration to // GenerateCertificate when actioning a certification generation request. type Request struct { Name string KeyName string IsClientCertificate bool - PrivateKeySize int + PrivateKeyOptions PrivateKeyOptions Template *x509.Certificate AllowOverwrite bool } @@ -101,13 +129,11 @@ func (e *ZitiPKI) Sign(signer *certificate.Bundle, req *Request) error { } var err error - var privateKey *rsa.PrivateKey + var privateKey crypto.PrivateKey if req.KeyName == "" { - if req.PrivateKeySize == 0 { - req.PrivateKeySize = defaultPrivateKeySize - } - privateKey, err = rsa.GenerateKey(rand.Reader, req.PrivateKeySize) + privateKey, err = req.PrivateKeyOptions.GenerateKey() + if err != nil { return fmt.Errorf("failed generating private key: %v", err) } @@ -118,7 +144,12 @@ func (e *ZitiPKI) Sign(signer *certificate.Bundle, req *Request) error { } privateKey = pk.(*rsa.PrivateKey) } - publicKey := privateKey.Public() + + publicKey, err := publicKeyFromPrivate(privateKey) + + if err != nil { + return err + } if err := defaultTemplate(req, publicKey); err != nil { return fmt.Errorf("failed updating generation request: %v", err) @@ -148,12 +179,29 @@ func (e *ZitiPKI) Sign(signer *certificate.Bundle, req *Request) error { return fmt.Errorf("failed creating and signing certificate: %v", err) } - if err := e.Store.Add(signer.Name, req.Name, req.Template.IsCA, x509.MarshalPKCS1PrivateKey(privateKey), rawCert, req.AllowOverwrite); err != nil { + keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + + if err != nil { + return nil + } + + if err := e.Store.Add(signer.Name, req.Name, req.Template.IsCA, keyBytes, rawCert, req.AllowOverwrite); err != nil { return fmt.Errorf("failed saving generated bundle: %v", err) } return nil } +func publicKeyFromPrivate(key crypto.PrivateKey) (crypto.PublicKey, error) { + switch pk := key.(type) { + case *rsa.PrivateKey: + return &pk.PublicKey, nil + case *ecdsa.PrivateKey: + return &pk.PublicKey, nil + } + + return nil, errors.New("unsupported key type") +} + // Chain will... func (e *ZitiPKI) Chain(signer *certificate.Bundle, req *Request) error { destCA := signer.Name @@ -166,16 +214,22 @@ func (e *ZitiPKI) Chain(signer *certificate.Bundle, req *Request) error { return nil } -// Generate and store a private key +// GeneratePrivateKey generates and stores a private key func (e *ZitiPKI) GeneratePrivateKey(signer *certificate.Bundle, req *Request) error { - if req.PrivateKeySize == 0 { - req.PrivateKeySize = defaultPrivateKeySize - } - privateKey, err := rsa.GenerateKey(rand.Reader, req.PrivateKeySize) + + privateKey, err := req.PrivateKeyOptions.GenerateKey() + if err != nil { return fmt.Errorf("failed generating private key: %v", err) } - if err := e.Store.AddKey(signer.Name, req.KeyName, x509.MarshalPKCS1PrivateKey(privateKey)); err != nil { + + keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + + if err != nil { + return fmt.Errorf("failed marshalling private key: %v", err) + } + + if err := e.Store.AddKey(signer.Name, req.KeyName, keyBytes); err != nil { return fmt.Errorf("failed saving generated key: %v", err) } return nil @@ -200,7 +254,20 @@ func (e *ZitiPKI) CRL(caName string, expire time.Time) ([]byte, error) { return nil, fmt.Errorf("failed retrieving CA bundle %v: %v", caName, err) } - crl, err := ca.Cert.CreateCRL(rand.Reader, ca.Key, revoked, time.Now(), expire) + signer, ok := ca.Key.(crypto.Signer) + + if !ok { + return nil, fmt.Errorf("could not convert %T to %T", ca.Key, signer) + } + + template := &x509.RevocationList{ + RevokedCertificates: revoked, + ThisUpdate: time.Now(), + NextUpdate: expire, + } + + crl, err := x509.CreateRevocationList(rand.Reader, template, ca.Cert, signer) + if err != nil { return nil, fmt.Errorf("failed creating crl for %v: %v", caName, err) } diff --git a/ziti/pki/pki/template.go b/ziti/pki/pki/template.go index 00d2d731a..4dcc30846 100644 --- a/ziti/pki/pki/template.go +++ b/ziti/pki/pki/template.go @@ -19,17 +19,16 @@ package pki import ( "crypto" "crypto/rand" - "crypto/rsa" "crypto/sha1" "crypto/x509" - "encoding/asn1" "fmt" "math/big" "time" ) func defaultTemplate(genReq *Request, publicKey crypto.PublicKey) error { - publicKeyBytes, err := asn1.Marshal(*publicKey.(*rsa.PublicKey)) + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { return fmt.Errorf("failed marshaling public key: %v", err) } @@ -45,7 +44,6 @@ func defaultTemplate(genReq *Request, publicKey crypto.PublicKey) error { genReq.Template.SerialNumber = sn genReq.Template.NotBefore = time.Now().Add(-time.Minute) - genReq.Template.SignatureAlgorithm = x509.SHA256WithRSA return nil } diff --git a/ziti/pki/store/local.go b/ziti/pki/store/local.go index 2cdea6ec4..017a1e960 100644 --- a/ziti/pki/store/local.go +++ b/ziti/pki/store/local.go @@ -18,6 +18,8 @@ package store import ( "bufio" + "crypto/ecdsa" + "crypto/rsa" "crypto/x509/pkix" "encoding/pem" "fmt" @@ -156,19 +158,22 @@ func (l *Local) AddCSR(caName, name string, isCa bool, key, cert []byte) error { return nil } -// AddKey adds the given key to the local filesystem. +// AddKey adds the given private key to the local filesystem. func (l *Local) AddKey(caName string, name string, key []byte) error { if l.Exists(caName, name) { return fmt.Errorf("a key already exists for the key name %v within CA %v", name, caName) } - if err := l.writeKey(caName, name, key); err != nil { + + pemType := getPkPemType(key) + + if err := l.writeKey(caName, name, pemType, key); err != nil { return fmt.Errorf("failed writing key %v within CA %v to the local filesystem: %v", name, caName, err) } return nil } // writeKey encodes in PEM format the bundle private key and stores it on the local filesystem. -func (l *Local) writeKey(caName string, name string, key []byte) error { +func (l *Local) writeKey(caName, name, pemType string, key []byte) error { caDir := filepath.Join(l.Root, caName) if _, err := os.Stat(caDir); err != nil { if err := InitCADir(caDir); err != nil { @@ -176,12 +181,26 @@ func (l *Local) writeKey(caName string, name string, key []byte) error { } } keyPath, _ := l.path(caName, name) - if err := encodeAndWrite(keyPath, "RSA PRIVATE KEY", key); err != nil { + if err := encodeAndWrite(keyPath, pemType, key); err != nil { return fmt.Errorf("failed encoding and writing private key file: %v", err) } return nil } +func getPkPemType(key []byte) string { + parsedKey, _ := x509.ParsePKCS8PrivateKey(key) + + keyPemType := "UNKNOWN PRIVATE KEY" + switch parsedKey.(type) { + case *rsa.PrivateKey: + keyPemType = "RSA PRIVATE KEY" + case *ecdsa.PrivateKey: + keyPemType = "EC PRIVATE KEY" + } + + return keyPemType +} + // writeBundle encodes in PEM format the bundle private key and // certificate and stores them on the local filesystem. func (l *Local) writeBundle(caName, name string, isCa bool, key, cert []byte) error { @@ -191,8 +210,11 @@ func (l *Local) writeBundle(caName, name string, isCa bool, key, cert []byte) er return fmt.Errorf("root directory for CA %v does not exist and cannot be created: %v", caDir, err) } } + + keyPemType := getPkPemType(key) + keyPath, certPath := l.path(caName, name) - if err := encodeAndWrite(keyPath, "RSA PRIVATE KEY", key); err != nil { + if err := encodeAndWrite(keyPath, keyPemType, key); err != nil { return fmt.Errorf("failed encoding and writing private key file: %v", err) } if err := encodeAndWrite(certPath, "CERTIFICATE", cert); err != nil { diff --git a/ziti/pki/store/store.go b/ziti/pki/store/store.go index 1060bb6b0..155a6e3e6 100644 --- a/ziti/pki/store/store.go +++ b/ziti/pki/store/store.go @@ -65,7 +65,7 @@ type Store interface { // Args: // The intermediate CA name // The Key name - // The raw private key. + // The private key // // Returns an error if it failed to store the bundle. AddKey(string, string, []byte) error From 9f0da4b97b33ec3b14ef5564e8b3954a592dc550 Mon Sep 17 00:00:00 2001 From: Andrew Martinez Date: Thu, 27 Jul 2023 15:54:02 -0400 Subject: [PATCH 2/3] use fallback PKCS1 parsing for private keys if PKCS8 fails --- ziti/pki/certificate/certificate.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ziti/pki/certificate/certificate.go b/ziti/pki/certificate/certificate.go index 6852ca9ab..9f86648b8 100644 --- a/ziti/pki/certificate/certificate.go +++ b/ziti/pki/certificate/certificate.go @@ -40,7 +40,14 @@ func (b *Bundle) Raw() ([]byte, []byte) { func RawToBundle(name string, key []byte, cert []byte) (*Bundle, error) { k, err := x509.ParsePKCS8PrivateKey(key) if err != nil { - return nil, fmt.Errorf("failed parsing private key: %v", err) + //previous versions may have stored private keys as PKCS1 (aka RSA only) try as a secondary action + var pkcs1Err error + k, pkcs1Err = x509.ParsePKCS1PrivateKey(key) + + if pkcs1Err != nil { + return nil, fmt.Errorf("failed parsing private key PKCS8 error: %w, PKCS1 error: %w", err, pkcs1Err) + } + } c, err := x509.ParseCertificate(cert) if err != nil { From 1bc8bcbee641084a1ba54b91a362886cd3e46fec Mon Sep 17 00:00:00 2001 From: Andrew Martinez Date: Fri, 28 Jul 2023 10:36:31 -0400 Subject: [PATCH 3/3] fix pkcs8 vs pkcs1 vs pkcs EC private key parsing --- ziti/cmd/edge/verify_ca.go | 22 ++++++++++++++++++++-- ziti/cmd/lets_encrypt/accounts_storage.go | 2 ++ ziti/pki/store/local.go | 22 ++++++++++++---------- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/ziti/cmd/edge/verify_ca.go b/ziti/cmd/edge/verify_ca.go index 2a6ea51aa..b702bf165 100644 --- a/ziti/cmd/edge/verify_ca.go +++ b/ziti/cmd/edge/verify_ca.go @@ -21,6 +21,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -200,10 +201,27 @@ func generateCert(options *verifyCaOptions, token string) ([]byte, crypto.Signer } var signerInterface crypto.Signer - if caPrivateKeyBlock.Type == "EC PRIVATE KEY" { + + switch caPrivateKeyBlock.Type { + case "EC PRIVATE KEY": signerInterface, err = x509.ParseECPrivateKey(caPrivateKeyBlock.Bytes) - } else { + case "RSA PRIVATE KEY": signerInterface, err = x509.ParsePKCS1PrivateKey(caPrivateKeyBlock.Bytes) + case "PRIVATE KEY": + key, err := x509.ParsePKCS8PrivateKey(caPrivateKeyBlock.Bytes) + + if err != nil { + return nil, nil, fmt.Errorf("could not parse PKCS8 key file (%s)", err) + } + + switch typedKey := key.(type) { + case *rsa.PrivateKey: + signerInterface = typedKey + case *ecdsa.PrivateKey: + signerInterface = typedKey + default: + return nil, nil, fmt.Errorf("unsupported private key parsed from PKCS8 %T", typedKey) + } } if err != nil { diff --git a/ziti/cmd/lets_encrypt/accounts_storage.go b/ziti/cmd/lets_encrypt/accounts_storage.go index 1eb73690e..47fcc614a 100644 --- a/ziti/cmd/lets_encrypt/accounts_storage.go +++ b/ziti/cmd/lets_encrypt/accounts_storage.go @@ -229,6 +229,8 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) { return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) case "EC PRIVATE KEY": return x509.ParseECPrivateKey(keyBlock.Bytes) + case "PRIVATE KEY": + return x509.ParsePKCS8PrivateKey(keyBlock.Bytes) } return nil, errors.New("unknown private key type") diff --git a/ziti/pki/store/local.go b/ziti/pki/store/local.go index 017a1e960..6047dc4a3 100644 --- a/ziti/pki/store/local.go +++ b/ziti/pki/store/local.go @@ -18,8 +18,6 @@ package store import ( "bufio" - "crypto/ecdsa" - "crypto/rsa" "crypto/x509/pkix" "encoding/pem" "fmt" @@ -188,17 +186,21 @@ func (l *Local) writeKey(caName, name, pemType string, key []byte) error { } func getPkPemType(key []byte) string { - parsedKey, _ := x509.ParsePKCS8PrivateKey(key) + var pkcs1Err error + _, pkcs1Err = x509.ParsePKCS1PrivateKey(key) - keyPemType := "UNKNOWN PRIVATE KEY" - switch parsedKey.(type) { - case *rsa.PrivateKey: - keyPemType = "RSA PRIVATE KEY" - case *ecdsa.PrivateKey: - keyPemType = "EC PRIVATE KEY" + if pkcs1Err == nil { + return "RSA PRIVATE KEY" } - return keyPemType + var pkcsEcErr error + _, pkcsEcErr = x509.ParseECPrivateKey(key) + + if pkcsEcErr == nil { + return "EC PRIVATE KEY" + } + + return "PRIVATE KEY" } // writeBundle encodes in PEM format the bundle private key and