@@ -8,11 +8,13 @@ package tlsconfig
8
8
import (
9
9
"crypto/tls"
10
10
"crypto/x509"
11
+ "encoding/pem"
11
12
"fmt"
12
13
"io/ioutil"
13
14
"os"
14
15
15
16
"github.com/Sirupsen/logrus"
17
+ "github.com/pkg/errors"
16
18
)
17
19
18
20
// Options represents the information needed to create client and server TLS configurations.
@@ -34,6 +36,9 @@ type Options struct {
34
36
// the system pool will be used.
35
37
ExclusiveRootPools bool
36
38
MinVersion uint16
39
+ // If Passphrase is set, it will be used to decrypt a TLS private key
40
+ // if the key is encrypted
41
+ Passphrase string
37
42
}
38
43
39
44
// Extra (server-side) accepted CBC cipher suites - will phase out in the future
@@ -127,6 +132,67 @@ func adjustMinVersion(options Options, config *tls.Config) error {
127
132
return nil
128
133
}
129
134
135
+ // IsErrEncryptedKey returns true if the 'err' is an error of incorrect
136
+ // password when tryin to decrypt a TLS private key
137
+ func IsErrEncryptedKey (err error ) bool {
138
+ return errors .Cause (err ) == x509 .IncorrectPasswordError
139
+ }
140
+
141
+ // getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format.
142
+ // If the private key is encrypted, 'passphrase' is used to decrypted the
143
+ // private key.
144
+ func getPrivateKey (keyBytes []byte , passphrase string ) ([]byte , error ) {
145
+ // this section makes some small changes to code from notary/tuf/utils/x509.go
146
+ pemBlock , _ := pem .Decode (keyBytes )
147
+ if pemBlock == nil {
148
+ return nil , fmt .Errorf ("no valid private key found" )
149
+ }
150
+
151
+ var err error
152
+ if x509 .IsEncryptedPEMBlock (pemBlock ) {
153
+ keyBytes , err = x509 .DecryptPEMBlock (pemBlock , []byte (passphrase ))
154
+ if err != nil {
155
+ return nil , errors .Wrap (err , "private key is encrypted, but could not decrypt it" )
156
+ }
157
+ keyBytes = pem .EncodeToMemory (& pem.Block {Type : pemBlock .Type , Bytes : keyBytes })
158
+ }
159
+
160
+ return keyBytes , nil
161
+ }
162
+
163
+ // getCert returns a Certificate from the CertFile and KeyFile in 'options',
164
+ // if the key is encrypted, the Passphrase in 'options' will be used to
165
+ // decrypt it.
166
+ func getCert (options Options ) ([]tls.Certificate , error ) {
167
+ if options .CertFile == "" && options .KeyFile == "" {
168
+ return nil , nil
169
+ }
170
+
171
+ errMessage := "Could not load X509 key pair"
172
+
173
+ cert , err := ioutil .ReadFile (options .CertFile )
174
+ if err != nil {
175
+ return nil , errors .Wrap (err , errMessage )
176
+ }
177
+
178
+ prKeyBytes , err := ioutil .ReadFile (options .KeyFile )
179
+ if err != nil {
180
+ return nil , errors .Wrap (err , errMessage )
181
+ }
182
+
183
+ prKeyBytes , err = getPrivateKey (prKeyBytes , options .Passphrase )
184
+ if err != nil {
185
+ return nil , errors .Wrap (err , errMessage )
186
+ }
187
+
188
+ tlsCert , err := tls .X509KeyPair (cert , prKeyBytes )
189
+ if err != nil {
190
+ return nil , errors .Wrap (err , errMessage )
191
+ }
192
+
193
+ return []tls.Certificate {tlsCert }, nil
194
+ }
195
+
130
196
// Client returns a TLS configuration meant to be used by a client.
131
197
func Client (options Options ) (* tls.Config , error ) {
132
198
tlsConfig := ClientDefault ()
@@ -139,13 +205,11 @@ func Client(options Options) (*tls.Config, error) {
139
205
tlsConfig .RootCAs = CAs
140
206
}
141
207
142
- if options .CertFile != "" || options .KeyFile != "" {
143
- tlsCert , err := tls .LoadX509KeyPair (options .CertFile , options .KeyFile )
144
- if err != nil {
145
- return nil , fmt .Errorf ("Could not load X509 key pair: %v. Make sure the key is not encrypted" , err )
146
- }
147
- tlsConfig .Certificates = []tls.Certificate {tlsCert }
208
+ tlsCerts , err := getCert (options )
209
+ if err != nil {
210
+ return nil , err
148
211
}
212
+ tlsConfig .Certificates = tlsCerts
149
213
150
214
if err := adjustMinVersion (options , tlsConfig ); err != nil {
151
215
return nil , err
0 commit comments