From fb1159d672898bf1692e0e4e1cf0b5f4ef8c59df Mon Sep 17 00:00:00 2001 From: jtv4k Date: Tue, 20 Mar 2018 16:21:10 -0700 Subject: [PATCH 1/8] Added SSLClientCertificateKeyPassword option to ClientOptions object. Handled decoding of password protected PEM files. --- mongo/client_options.go | 6 ++ mongo/client_options_test.go | 1 + mongo/connstring/connstring.go | 95 ++++++++++++++++--------------- mongo/private/cluster/options.go | 3 + mongo/private/conn/tls_options.go | 68 +++++++++++++++++----- mongo/private/options/client.go | 10 ++++ 6 files changed, 123 insertions(+), 60 deletions(-) diff --git a/mongo/client_options.go b/mongo/client_options.go index acc91a3320..1459575dbd 100644 --- a/mongo/client_options.go +++ b/mongo/client_options.go @@ -146,6 +146,12 @@ func (co *ClientOptions) SSLClientCertificateKeyFile(s string) *ClientOptions { return &ClientOptions{next: co, opt: options.SSLClientCertificateKeyFile(s), err: nil} } +// SSLClientCertificateKeyPassword provides a callback that returns a password used for decrypting the +// private key of a PEM file (if one is provided). +func (co *ClientOptions) SSLClientCertificateKeyPassword(s func() string) *ClientOptions { + return &ClientOptions{next: co, opt: options.SSLClientCertificateKeyPassword(s), err: nil} +} + // SSLInsecure indicates whether to skip the verification of the server certificate and hostname. func (co *ClientOptions) SSLInsecure(b bool) *ClientOptions { return &ClientOptions{next: co, opt: options.SSLInsecure(b), err: nil} diff --git a/mongo/client_options_test.go b/mongo/client_options_test.go index 737bdf477e..2fe56a4fa3 100644 --- a/mongo/client_options_test.go +++ b/mongo/client_options_test.go @@ -68,6 +68,7 @@ func TestClientOptions_chainAll(t *testing.T) { SocketTimeout(2 * time.Second). SSL(true). SSLClientCertificateKeyFile("client.pem"). + SSLClientCertificateKeyPassword(func() string { return "password" }). SSLInsecure(false). SSLCaFile("ca.pem"). WString("majority"). diff --git a/mongo/connstring/connstring.go b/mongo/connstring/connstring.go index 1f7ae5868b..f3f08b7b35 100644 --- a/mongo/connstring/connstring.go +++ b/mongo/connstring/connstring.go @@ -32,52 +32,53 @@ func Parse(s string) (ConnString, error) { // ConnString represents a connection string to mongodb. type ConnString struct { - Original string - AppName string - AuthMechanism string - AuthMechanismProperties map[string]string - AuthSource string - Connect ConnectMode - ConnectSet bool - ConnectTimeout time.Duration - ConnectTimeoutSet bool - Database string - HeartbeatInterval time.Duration - HeartbeatIntervalSet bool - Hosts []string - J bool - JSet bool - LocalThreshold time.Duration - LocalThresholdSet bool - MaxConnIdleTime time.Duration - MaxConnIdleTimeSet bool - MaxConnLifeTime time.Duration - MaxConnsPerHost uint16 - MaxConnsPerHostSet bool - MaxIdleConnsPerHost uint16 - MaxIdleConnsPerHostSet bool - Password string - PasswordSet bool - ReadConcernLevel string - ReadPreference string - ReadPreferenceTagSets []map[string]string - ReplicaSet string - ServerSelectionTimeout time.Duration - ServerSelectionTimeoutSet bool - SocketTimeout time.Duration - SocketTimeoutSet bool - SSL bool - SSLSet bool - SSLClientCertificateKeyFile string - SSLClientCertificateKeyFileSet bool - SSLInsecure bool - SSLInsecureSet bool - SSLCaFile string - SSLCaFileSet bool - WString string - WNumber int - WNumberSet bool - Username string + Original string + AppName string + AuthMechanism string + AuthMechanismProperties map[string]string + AuthSource string + Connect ConnectMode + ConnectSet bool + ConnectTimeout time.Duration + ConnectTimeoutSet bool + Database string + HeartbeatInterval time.Duration + HeartbeatIntervalSet bool + Hosts []string + J bool + JSet bool + LocalThreshold time.Duration + LocalThresholdSet bool + MaxConnIdleTime time.Duration + MaxConnIdleTimeSet bool + MaxConnLifeTime time.Duration + MaxConnsPerHost uint16 + MaxConnsPerHostSet bool + MaxIdleConnsPerHost uint16 + MaxIdleConnsPerHostSet bool + Password string + PasswordSet bool + ReadConcernLevel string + ReadPreference string + ReadPreferenceTagSets []map[string]string + ReplicaSet string + ServerSelectionTimeout time.Duration + ServerSelectionTimeoutSet bool + SocketTimeout time.Duration + SocketTimeoutSet bool + SSL bool + SSLSet bool + SSLClientCertificateKeyFile string + SSLClientCertificateKeyPassword func() string + SSLClientCertificateKeyFileSet bool + SSLInsecure bool + SSLInsecureSet bool + SSLCaFile string + SSLCaFileSet bool + WString string + WNumber int + WNumberSet bool + Username string WTimeout time.Duration WTimeoutSet bool @@ -472,6 +473,8 @@ func (p *parser) addOption(pair string) error { p.SSLSet = true p.SSLClientCertificateKeyFile = value p.SSLClientCertificateKeyFileSet = true + case "sslclientcertificatekeypassword": + p.SSLClientCertificateKeyPassword = func() string { return value } case "sslinsecure": switch value { case "true": diff --git a/mongo/private/cluster/options.go b/mongo/private/cluster/options.go index 3f4d4c0046..c0ad8dceb8 100644 --- a/mongo/private/cluster/options.go +++ b/mongo/private/cluster/options.go @@ -119,6 +119,9 @@ func WithConnString(cs connstring.ConnString) Option { } if cs.SSLClientCertificateKeyFileSet { + if cs.SSLClientCertificateKeyPassword != nil { + tls.SetClientCertDecryptPassword(cs.SSLClientCertificateKeyPassword) + } s, err := tls.AddClientCertFromFile(cs.SSLClientCertificateKeyFile) if err != nil { return err diff --git a/mongo/private/conn/tls_options.go b/mongo/private/conn/tls_options.go index 1bdca3d587..eb76c8382f 100644 --- a/mongo/private/conn/tls_options.go +++ b/mongo/private/conn/tls_options.go @@ -7,6 +7,7 @@ package conn import ( + "bytes" "crypto/tls" "crypto/x509" "encoding/asn1" @@ -14,14 +15,16 @@ import ( "encoding/pem" "fmt" "io/ioutil" + "strings" ) // TLSConfig contains options for configuring an SSL connection to the server. type TLSConfig struct { - caCert *x509.Certificate - clientCert tls.Certificate - clientCertSet bool - insecure bool + caCert *x509.Certificate + clientCert tls.Certificate + clientCertPass func() string + clientCertSet bool + insecure bool } // NewTLSConfig creates a new TLSConfig. @@ -36,6 +39,10 @@ func (c *TLSConfig) SetInsecure(allow bool) { c.insecure = allow } +func (c *TLSConfig) SetClientCertDecryptPassword(f func() string) { + c.clientCertPass = f +} + // AddClientCertFromFile adds a client certificate to the configuration given a path to the // containing file and returns the certificate's subject name. func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { @@ -44,7 +51,47 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { return "", err } - cert, err := tls.X509KeyPair(data, data) + var ( + currentBlock *pem.Block + certBlock, certDecodedBlock, keyBlock []byte + ) + remaining := data + start := 0 + for { + currentBlock, remaining = pem.Decode(remaining) + if currentBlock == nil { + break + } + + if currentBlock.Type == "CERTIFICATE" { + // Maintain a copy of + if len(certBlock) != 0 { + return "", fmt.Errorf("multiple CERTIFICATE sections in .pem file") + } + + certBlock = data[start : len(data)-len(remaining)-1] + certDecodedBlock = currentBlock.Bytes + start += len(certBlock) + } else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") { + if c.clientCertPass != nil && x509.IsEncryptedPEMBlock(currentBlock) { + var encoded bytes.Buffer + buf, err := x509.DecryptPEMBlock(currentBlock, []byte(c.clientCertPass())) + if err != nil { + return "", err + } + + pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: buf}) + keyBlock = encoded.Bytes() + start = len(data) - len(remaining) + } else { + keyBlock = data[start : len(data)-len(remaining)] + start += len(keyBlock) + certDecodedBlock = currentBlock.Bytes + } + } + } + + cert, err := tls.X509KeyPair(certBlock, keyBlock) if err != nil { return "", err } @@ -53,15 +100,8 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { c.clientCertSet = true // The documentation for the tls.X509KeyPair indicates that the Leaf certificate is not - // retained. Because there isn't any way of creating a tls.Certificate from an x509.Certificate - // short of calling X509KeyPair on the raw bytes, we're forced to parse the certificate over - // again to get the subject name. - certBytes, err := loadCert(data) - if err != nil { - return "", err - } - - crt, err := x509.ParseCertificate(certBytes) + // retained. + crt, err := x509.ParseCertificate(certDecodedBlock) if err != nil { return "", err } diff --git a/mongo/private/options/client.go b/mongo/private/options/client.go index a97a1576ae..6630dee0f1 100644 --- a/mongo/private/options/client.go +++ b/mongo/private/options/client.go @@ -287,6 +287,16 @@ func (scckf SSLClientCertificateKeyFile) ClientOption(cs *connstring.ConnString) } } +// SSLClientCertificateKeyPassword for internal use. +type SSLClientCertificateKeyPassword func() string + +// ClientOption implements the ClientOption interface. +func (scckp SSLClientCertificateKeyPassword) ClientOption(cs *connstring.ConnString) { + if scckp != nil { + cs.SSLClientCertificateKeyPassword = scckp + } +} + // SSLInsecure is for internal use. type SSLInsecure bool From 79322c789f2544be0ca7cdc757f7b595a47d9b38 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Fri, 6 Apr 2018 10:16:37 -0700 Subject: [PATCH 2/8] Add password decoding to SSL certificates Added SSLClientCertificateKeyPassword option to ClientOptions object and handled decoding of protected PEM files. --- core/connection/tlsconfig.go | 67 ++++++++++++++++++++++++++----- core/options/client.go | 1 + core/topology/topology_options.go | 3 ++ 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 337e3e9a17..02841f54da 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -1,6 +1,7 @@ package connection import ( + "bytes" "crypto/tls" "crypto/x509" "encoding/asn1" @@ -9,10 +10,14 @@ import ( "errors" "fmt" "io/ioutil" + "strings" ) // TLSConfig contains options for configuring a TLS connection to the server. -type TLSConfig struct{ *tls.Config } +type TLSConfig struct { + *tls.Config + clientCertPass func() string +} // NewTLSConfig creates a new TLSConfig. func NewTLSConfig() *TLSConfig { @@ -22,6 +27,13 @@ func NewTLSConfig() *TLSConfig { return cfg } +// SetClientCertDecryptPassword sets a function to retrieve the decryption password +// necessary to read a certificate. This is a function instead of a string to +// provide greater flexibility when deciding how to retrieve and store the password. +func (c *TLSConfig) SetClientCertDecryptPassword(f func() string) { + c.clientCertPass = f +} + // SetInsecure sets whether the client should verify the server's certificate // chain and hostnames. func (c *TLSConfig) SetInsecure(allow bool) { @@ -63,7 +75,47 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { return "", err } - cert, err := tls.X509KeyPair(data, data) + var ( + currentBlock *pem.Block + certBlock, certDecodedBlock, keyBlock []byte + ) + remaining := data + start := 0 + for { + currentBlock, remaining = pem.Decode(remaining) + if currentBlock == nil { + break + } + + if currentBlock.Type == "CERTIFICATE" { + // Maintain a copy of + if len(certBlock) != 0 { + return "", fmt.Errorf("multiple CERTIFICATE sections in .pem file") + } + + certBlock = data[start : len(data)-len(remaining)-1] + certDecodedBlock = currentBlock.Bytes + start += len(certBlock) + } else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") { + if c.clientCertPass != nil && x509.IsEncryptedPEMBlock(currentBlock) { + var encoded bytes.Buffer + buf, err := x509.DecryptPEMBlock(currentBlock, []byte(c.clientCertPass())) + if err != nil { + return "", err + } + + pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: buf}) + keyBlock = encoded.Bytes() + start = len(data) - len(remaining) + } else { + keyBlock = data[start : len(data)-len(remaining)] + start += len(keyBlock) + certDecodedBlock = currentBlock.Bytes + } + } + } + + cert, err := tls.X509KeyPair(certBlock, keyBlock) if err != nil { return "", err } @@ -71,15 +123,8 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { c.Certificates = append(c.Certificates, cert) // The documentation for the tls.X509KeyPair indicates that the Leaf certificate is not - // retained. Because there isn't any way of creating a tls.Certificate from an x509.Certificate - // short of calling X509KeyPair on the raw bytes, we're forced to parse the certificate over - // again to get the subject name. - certBytes, err := loadCert(data) - if err != nil { - return "", err - } - - crt, err := x509.ParseCertificate(certBytes) + // retained. + crt, err := x509.ParseCertificate(certDecodedBlock) if err != nil { return "", err } diff --git a/core/options/client.go b/core/options/client.go index 9b6e027318..f4f76c2e8a 100644 --- a/core/options/client.go +++ b/core/options/client.go @@ -41,6 +41,7 @@ var ( _ ClientOptioner = (*SocketTimeout)(nil) _ ClientOptioner = (*SSL)(nil) _ ClientOptioner = (*SSLClientCertificateKeyFile)(nil) + _ ClientOptioner = (*SSLClientCertificateKeyPassword)(nil) _ ClientOptioner = (*SSLInsecure)(nil) _ ClientOptioner = (*SSLCaFile)(nil) _ ClientOptioner = (*WString)(nil) diff --git a/core/topology/topology_options.go b/core/topology/topology_options.go index b66a334880..719137985d 100644 --- a/core/topology/topology_options.go +++ b/core/topology/topology_options.go @@ -101,6 +101,9 @@ func WithConnString(fn func(connstring.ConnString) connstring.ConnString) Option } if cs.SSLClientCertificateKeyFileSet { + if cs.SSLClientCertificateKeyPassword != nil { + tlsConfig.SetClientCertDecryptPassword(cs.SSLClientCertificateKeyPassword) + } s, err := tlsConfig.AddClientCertFromFile(cs.SSLClientCertificateKeyFile) if err != nil { return err From a2e6ff1c681c50793865bfa12dfddbf502f6d163 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Mon, 9 Apr 2018 11:48:27 -0700 Subject: [PATCH 3/8] Initialize TLSConfig properly Fixed a compiler error. --- core/connection/tlsconfig_clone_17.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/connection/tlsconfig_clone_17.go b/core/connection/tlsconfig_clone_17.go index c5ff6d4cbf..15fbb61d94 100644 --- a/core/connection/tlsconfig_clone_17.go +++ b/core/connection/tlsconfig_clone_17.go @@ -6,7 +6,7 @@ import "crypto/tls" // used concurrently by a TLS client or server. func (c *TLSConfig) Clone() *TLSConfig { cfg := cloneconfig(c.Config) - return &TLSConfig{cfg} + return &TLSConfig{cfg, nil} } func cloneconfig(c *tls.Config) *tls.Config { From 2922c71a7f885e0e7f7e9a70c7c07cb52cf778c6 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Mon, 9 Apr 2018 13:29:47 -0700 Subject: [PATCH 4/8] Target new options design Fixed issue caused when merging from the old options design to the new. Fixed a clone option where passwords did not get copied. --- core/connection/tlsconfig.go | 7 +- core/connection/tlsconfig_clone_17.go | 2 +- core/connstring/connstring.go | 96 ++++++++++++++------------- core/topology/topology_options.go | 2 +- mongo/client_options.go | 9 ++- 5 files changed, 62 insertions(+), 54 deletions(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 02841f54da..62dd186c46 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -75,10 +75,9 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { return "", err } - var ( - currentBlock *pem.Block - certBlock, certDecodedBlock, keyBlock []byte - ) + var currentBlock *pem.Block + var certBlock, certDecodedBlock, keyBlock []byte + remaining := data start := 0 for { diff --git a/core/connection/tlsconfig_clone_17.go b/core/connection/tlsconfig_clone_17.go index 15fbb61d94..4b52364fdc 100644 --- a/core/connection/tlsconfig_clone_17.go +++ b/core/connection/tlsconfig_clone_17.go @@ -6,7 +6,7 @@ import "crypto/tls" // used concurrently by a TLS client or server. func (c *TLSConfig) Clone() *TLSConfig { cfg := cloneconfig(c.Config) - return &TLSConfig{cfg, nil} + return &TLSConfig{cfg, c.clientCertPass} } func cloneconfig(c *tls.Config) *tls.Config { diff --git a/core/connstring/connstring.go b/core/connstring/connstring.go index 1420e0f9ce..aa9524576c 100644 --- a/core/connstring/connstring.go +++ b/core/connstring/connstring.go @@ -32,53 +32,54 @@ func Parse(s string) (ConnString, error) { // ConnString represents a connection string to mongodb. type ConnString struct { - Original string - AppName string - AuthMechanism string - AuthMechanismProperties map[string]string - AuthSource string - Connect ConnectMode - ConnectSet bool - ConnectTimeout time.Duration - ConnectTimeoutSet bool - Database string - HeartbeatInterval time.Duration - HeartbeatIntervalSet bool - Hosts []string - J bool - JSet bool - LocalThreshold time.Duration - LocalThresholdSet bool - MaxConnIdleTime time.Duration - MaxConnIdleTimeSet bool - MaxConnLifeTime time.Duration - MaxConnsPerHost uint16 - MaxConnsPerHostSet bool - MaxIdleConnsPerHost uint16 - MaxIdleConnsPerHostSet bool - Password string - PasswordSet bool - ReadConcernLevel string - ReadPreference string - ReadPreferenceTagSets []map[string]string - ReplicaSet string - ServerSelectionTimeout time.Duration - ServerSelectionTimeoutSet bool - SocketTimeout time.Duration - SocketTimeoutSet bool - SSL bool - SSLSet bool - SSLClientCertificateKeyFile string - SSLClientCertificateKeyPassword func() string - SSLClientCertificateKeyFileSet bool - SSLInsecure bool - SSLInsecureSet bool - SSLCaFile string - SSLCaFileSet bool - WString string - WNumber int - WNumberSet bool - Username string + Original string + AppName string + AuthMechanism string + AuthMechanismProperties map[string]string + AuthSource string + Connect ConnectMode + ConnectSet bool + ConnectTimeout time.Duration + ConnectTimeoutSet bool + Database string + HeartbeatInterval time.Duration + HeartbeatIntervalSet bool + Hosts []string + J bool + JSet bool + LocalThreshold time.Duration + LocalThresholdSet bool + MaxConnIdleTime time.Duration + MaxConnIdleTimeSet bool + MaxConnLifeTime time.Duration + MaxConnsPerHost uint16 + MaxConnsPerHostSet bool + MaxIdleConnsPerHost uint16 + MaxIdleConnsPerHostSet bool + Password string + PasswordSet bool + ReadConcernLevel string + ReadPreference string + ReadPreferenceTagSets []map[string]string + ReplicaSet string + ServerSelectionTimeout time.Duration + ServerSelectionTimeoutSet bool + SocketTimeout time.Duration + SocketTimeoutSet bool + SSL bool + SSLSet bool + SSLClientCertificateKeyFile string + SSLClientCertificateKeyFileSet bool + SSLClientCertificateKeyPassword func() string + SSLClientCertificateKeyPasswordSet bool + SSLInsecure bool + SSLInsecureSet bool + SSLCaFile string + SSLCaFileSet bool + WString string + WNumber int + WNumberSet bool + Username string WTimeout time.Duration WTimeoutSet bool @@ -475,6 +476,7 @@ func (p *parser) addOption(pair string) error { p.SSLClientCertificateKeyFileSet = true case "sslclientcertificatekeypassword": p.SSLClientCertificateKeyPassword = func() string { return value } + p.SSLClientCertificateKeyPasswordSet = true case "sslinsecure": switch value { case "true": diff --git a/core/topology/topology_options.go b/core/topology/topology_options.go index 719137985d..2361e19d3f 100644 --- a/core/topology/topology_options.go +++ b/core/topology/topology_options.go @@ -101,7 +101,7 @@ func WithConnString(fn func(connstring.ConnString) connstring.ConnString) Option } if cs.SSLClientCertificateKeyFileSet { - if cs.SSLClientCertificateKeyPassword != nil { + if cs.SSLClientCertificateKeyPasswordSet && cs.SSLClientCertificateKeyPassword != nil { tlsConfig.SetClientCertDecryptPassword(cs.SSLClientCertificateKeyPassword) } s, err := tlsConfig.AddClientCertFromFile(cs.SSLClientCertificateKeyFile) diff --git a/mongo/client_options.go b/mongo/client_options.go index 80bfea37a1..16491f28e1 100644 --- a/mongo/client_options.go +++ b/mongo/client_options.go @@ -326,7 +326,14 @@ func (co *ClientOptions) SSLClientCertificateKeyFile(s string) *ClientOptions { // SSLClientCertificateKeyPassword provides a callback that returns a password used for decrypting the // private key of a PEM file (if one is provided). func (co *ClientOptions) SSLClientCertificateKeyPassword(s func() string) *ClientOptions { - return &ClientOptions{next: co, opt: options.SSLClientCertificateKeyPassword(s), err: nil} + var fn option = func(c *Client) error { + if !c.connString.SSLClientCertificateKeyPasswordSet { + c.connString.SSLClientCertificateKeyPassword = s + c.connString.SSLClientCertificateKeyPasswordSet = true + } + return nil + } + return &ClientOptions{next: co, opt: fn} } // SSLInsecure indicates whether to skip the verification of the server certificate and hostname. From 4c5f14da208ccb15ec926a049967adf5389d2575 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Mon, 9 Apr 2018 13:51:50 -0700 Subject: [PATCH 5/8] Remove cert block reset from private key section --- core/connection/tlsconfig.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 62dd186c46..0d37b2488c 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -109,7 +109,6 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { } else { keyBlock = data[start : len(data)-len(remaining)] start += len(keyBlock) - certDecodedBlock = currentBlock.Bytes } } } From 07d17c301443ef8ff59876ddd61b1942469d82e4 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Mon, 9 Apr 2018 15:01:35 -0700 Subject: [PATCH 6/8] Fix error on incorrectly ordered PEM files --- core/connection/tlsconfig.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 0d37b2488c..4fa92fda81 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -87,15 +87,13 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { } if currentBlock.Type == "CERTIFICATE" { - // Maintain a copy of - if len(certBlock) != 0 { - return "", fmt.Errorf("multiple CERTIFICATE sections in .pem file") - } - - certBlock = data[start : len(data)-len(remaining)-1] + certBlock = data[start : len(data)-len(remaining)] certDecodedBlock = currentBlock.Bytes start += len(certBlock) } else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") { + if len(certBlock) == 0 { + return "", fmt.Errorf("failed to find CERTIFICATE but did find private key; PEM inputs may be switched") + } if c.clientCertPass != nil && x509.IsEncryptedPEMBlock(currentBlock) { var encoded bytes.Buffer buf, err := x509.DecryptPEMBlock(currentBlock, []byte(c.clientCertPass())) @@ -107,7 +105,7 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { keyBlock = encoded.Bytes() start = len(data) - len(remaining) } else { - keyBlock = data[start : len(data)-len(remaining)] + keyBlock = data[start:len(data)-len(remaining)] start += len(keyBlock) } } From d3ebe37297538883c4b39f6c017d084d54f45af1 Mon Sep 17 00:00:00 2001 From: jtv4k Date: Tue, 10 Apr 2018 11:39:58 -0700 Subject: [PATCH 7/8] Run go fmt on tlsconfig.go --- core/connection/tlsconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 4fa92fda81..883852a486 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -105,7 +105,7 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { keyBlock = encoded.Bytes() start = len(data) - len(remaining) } else { - keyBlock = data[start:len(data)-len(remaining)] + keyBlock = data[start : len(data)-len(remaining)] start += len(keyBlock) } } From b9b5bc123c0708501763a935fb1a7c0c5b8c4985 Mon Sep 17 00:00:00 2001 From: Kris Brandow Date: Tue, 10 Apr 2018 15:26:01 -0400 Subject: [PATCH 8/8] Allow blocks in pem files in any order GODRIVER-287 Closes #39 Change-Id: Id3cb2d6d2143844b5a27d3b45f967220caaff48c --- core/connection/tlsconfig.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/connection/tlsconfig.go b/core/connection/tlsconfig.go index 883852a486..daf4a25d72 100644 --- a/core/connection/tlsconfig.go +++ b/core/connection/tlsconfig.go @@ -91,9 +91,6 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { certDecodedBlock = currentBlock.Bytes start += len(certBlock) } else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") { - if len(certBlock) == 0 { - return "", fmt.Errorf("failed to find CERTIFICATE but did find private key; PEM inputs may be switched") - } if c.clientCertPass != nil && x509.IsEncryptedPEMBlock(currentBlock) { var encoded bytes.Buffer buf, err := x509.DecryptPEMBlock(currentBlock, []byte(c.clientCertPass())) @@ -110,6 +107,12 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) { } } } + if len(certBlock) == 0 { + return "", fmt.Errorf("failed to find CERTIFICATE") + } + if len(keyBlock) == 0 { + return "", fmt.Errorf("failed to find PRIVATE KEY") + } cert, err := tls.X509KeyPair(certBlock, keyBlock) if err != nil {