11package connection
22
33import (
4+ "bytes"
45 "crypto/tls"
56 "crypto/x509"
67 "encoding/asn1"
@@ -9,10 +10,14 @@ import (
910 "errors"
1011 "fmt"
1112 "io/ioutil"
13+ "strings"
1214)
1315
1416// TLSConfig contains options for configuring a TLS connection to the server.
15- type TLSConfig struct { * tls.Config }
17+ type TLSConfig struct {
18+ * tls.Config
19+ clientCertPass func () string
20+ }
1621
1722// NewTLSConfig creates a new TLSConfig.
1823func NewTLSConfig () * TLSConfig {
@@ -22,6 +27,13 @@ func NewTLSConfig() *TLSConfig {
2227 return cfg
2328}
2429
30+ // SetClientCertDecryptPassword sets a function to retrieve the decryption password
31+ // necessary to read a certificate. This is a function instead of a string to
32+ // provide greater flexibility when deciding how to retrieve and store the password.
33+ func (c * TLSConfig ) SetClientCertDecryptPassword (f func () string ) {
34+ c .clientCertPass = f
35+ }
36+
2537// SetInsecure sets whether the client should verify the server's certificate
2638// chain and hostnames.
2739func (c * TLSConfig ) SetInsecure (allow bool ) {
@@ -63,23 +75,55 @@ func (c *TLSConfig) AddClientCertFromFile(clientFile string) (string, error) {
6375 return "" , err
6476 }
6577
66- cert , err := tls .X509KeyPair (data , data )
78+ var currentBlock * pem.Block
79+ var certBlock , certDecodedBlock , keyBlock []byte
80+
81+ remaining := data
82+ start := 0
83+ for {
84+ currentBlock , remaining = pem .Decode (remaining )
85+ if currentBlock == nil {
86+ break
87+ }
88+
89+ if currentBlock .Type == "CERTIFICATE" {
90+ certBlock = data [start : len (data )- len (remaining )]
91+ certDecodedBlock = currentBlock .Bytes
92+ start += len (certBlock )
93+ } else if strings .HasSuffix (currentBlock .Type , "PRIVATE KEY" ) {
94+ if c .clientCertPass != nil && x509 .IsEncryptedPEMBlock (currentBlock ) {
95+ var encoded bytes.Buffer
96+ buf , err := x509 .DecryptPEMBlock (currentBlock , []byte (c .clientCertPass ()))
97+ if err != nil {
98+ return "" , err
99+ }
100+
101+ pem .Encode (& encoded , & pem.Block {Type : currentBlock .Type , Bytes : buf })
102+ keyBlock = encoded .Bytes ()
103+ start = len (data ) - len (remaining )
104+ } else {
105+ keyBlock = data [start : len (data )- len (remaining )]
106+ start += len (keyBlock )
107+ }
108+ }
109+ }
110+ if len (certBlock ) == 0 {
111+ return "" , fmt .Errorf ("failed to find CERTIFICATE" )
112+ }
113+ if len (keyBlock ) == 0 {
114+ return "" , fmt .Errorf ("failed to find PRIVATE KEY" )
115+ }
116+
117+ cert , err := tls .X509KeyPair (certBlock , keyBlock )
67118 if err != nil {
68119 return "" , err
69120 }
70121
71122 c .Certificates = append (c .Certificates , cert )
72123
73124 // The documentation for the tls.X509KeyPair indicates that the Leaf certificate is not
74- // retained. Because there isn't any way of creating a tls.Certificate from an x509.Certificate
75- // short of calling X509KeyPair on the raw bytes, we're forced to parse the certificate over
76- // again to get the subject name.
77- certBytes , err := loadCert (data )
78- if err != nil {
79- return "" , err
80- }
81-
82- crt , err := x509 .ParseCertificate (certBytes )
125+ // retained.
126+ crt , err := x509 .ParseCertificate (certDecodedBlock )
83127 if err != nil {
84128 return "" , err
85129 }
0 commit comments