forked from cloudflare/cfssl
/
key_provider.go
409 lines (344 loc) · 10.7 KB
/
key_provider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
// Package kp describes transport key providers and provides a reference
// implementation.
//
// KeyProviders are used by clients and servers as a mechanism for
// providing keys and signing CSRs. It is a mechanism designed to
// allow switching out how private keys and their associated
// certificates are managed, such as supporting PKCS #11. The
// StandardProvider provides disk-backed PEM-encoded certificates and
// private keys. DiskFallback is a provider that will attempt to
// retrieve the certificate from a CA first, falling back to a
// disk-backed pair. This is useful for test a CA while providing a
// failover solution.
package kp
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"strings"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/transport/core"
)
const (
curveP256 = 256
curveP384 = 384
curveP521 = 521
)
// A KeyProvider provides some mechanism for managing private keys and
// certificates. It is not required to store the crypto.Signer itself.
type KeyProvider interface {
// Certificate returns the associated certificate, or nil if
// one isn't ready.
Certificate() *x509.Certificate
// Given some metadata about a certificate request, the
// provider should be able to generate a new CSR.
CertificateRequest(*csr.CertificateRequest) ([]byte, error)
// Check returns an error if the provider has an invalid setup.
Check() error
// Generate should trigger the creation of a new private
// key. This will invalidate any certificates stored in the
// key provider.
Generate(algo string, size int) error
// Load causes a private key and certificate associated with
// this provider to be loaded into memory and be prepared for
// use.
Load() error
// Persistent returns true if the provider keeps state on disk.
Persistent() bool
// Ready returns true if the provider has a key and
// certificate.
Ready() bool
// SetCertificatePEM takes a PEM-encoded certificate and
// associates it with this key provider.
SetCertificatePEM([]byte) error
// SignalFailure is used to notify the KeyProvider that an
// error has occurred obtaining a certificate. If this returns
// true, the caller should re-attempt to refresh the
// keys. This, for example, can be used to implement failover
// key providers that require different keys.
SignalFailure(err error) bool
// SignCSR allows a templated CSR to be signed.
SignCSR(csr *x509.CertificateRequest) ([]byte, error)
// Store should perform whatever actions are necessary such
// that a call to Load later will reload the key and
// certificate associated with this provider.
Store() error
// X509KeyPair returns a tls.Certficate. The returns
// tls.Certificate should have a parsed Leaf certificate.
X509KeyPair() (tls.Certificate, error)
}
// StandardPaths contains a path to a key file and certificate file.
type StandardPaths struct {
KeyFile string `json:"private_key"`
CertFile string `json:"certificate"`
}
// StandardProvider provides unencrypted PEM-encoded certificates and
// private keys. If paths are provided, the key and certificate will
// be stored on disk.
type StandardProvider struct {
Paths StandardPaths `json:"paths"`
internal struct {
priv crypto.Signer
cert *x509.Certificate
// The PEM-encoded private key and certificate. This
// is stored alongside the crypto.Signer and
// x509.Certificate for convenience in marshaling and
// calling tls.X509KeyPair directly.
keyPEM []byte
certPEM []byte
}
}
// NewStandardProvider sets up new StandardProvider from the
// information contained in an Identity.
func NewStandardProvider(id *core.Identity) (*StandardProvider, error) {
if id == nil {
return nil, errors.New("transport: the identity hasn't been initialised. Has it been loaded from disk?")
}
paths := id.Profiles["paths"]
if paths == nil {
return &StandardProvider{}, nil
}
sp := &StandardProvider{
Paths: StandardPaths{
KeyFile: paths["private_key"],
CertFile: paths["certificate"],
},
}
err := sp.Check()
if err != nil {
return nil, err
}
return sp, nil
}
func (sp *StandardProvider) resetCert() {
sp.internal.cert = nil
sp.internal.certPEM = nil
}
func (sp *StandardProvider) resetKey() {
sp.internal.priv = nil
sp.internal.keyPEM = nil
}
var (
// ErrMissingKeyPath is returned if the StandardProvider has
// specified a certificate path but not a key path.
ErrMissingKeyPath = errors.New("transport: standard provider is missing a private key path to accompany the certificate path")
// ErrMissingCertPath is returned if the StandardProvider has
// specified a private key path but not a certificate path.
ErrMissingCertPath = errors.New("transport: standard provider is missing a certificate path to accompany the certificate path")
)
// Check ensures that the paths are valid for the provider.
func (sp *StandardProvider) Check() error {
if sp.Paths.KeyFile == "" && sp.Paths.CertFile == "" {
return nil
}
if sp.Paths.KeyFile == "" {
return ErrMissingKeyPath
}
if sp.Paths.CertFile == "" {
return ErrMissingCertPath
}
return nil
}
// Persistent returns true if the key and certificate will be stored
// on disk.
func (sp *StandardProvider) Persistent() bool {
return sp.Paths.KeyFile != "" && sp.Paths.CertFile != ""
}
// Generate generates a new private key.
func (sp *StandardProvider) Generate(algo string, size int) (err error) {
sp.resetKey()
sp.resetCert()
algo = strings.ToLower(algo)
switch algo {
case "rsa":
var priv *rsa.PrivateKey
if size < 2048 {
return errors.New("transport: RSA keys must be at least 2048 bits")
}
priv, err = rsa.GenerateKey(rand.Reader, size)
if err != nil {
return err
}
keyPEM := x509.MarshalPKCS1PrivateKey(priv)
p := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: keyPEM,
}
sp.internal.keyPEM = pem.EncodeToMemory(p)
sp.internal.priv = priv
case "ecdsa":
var priv *ecdsa.PrivateKey
var curve elliptic.Curve
switch size {
case curveP256:
curve = elliptic.P256()
case curveP384:
curve = elliptic.P384()
case curveP521:
curve = elliptic.P521()
default:
return errors.New("transport: invalid elliptic curve key size; only 256-, 384-, and 521-bit keys are accepted")
}
priv, err = ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return err
}
var keyPEM []byte
keyPEM, err = x509.MarshalECPrivateKey(priv)
if err != nil {
return err
}
p := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyPEM,
}
sp.internal.keyPEM = pem.EncodeToMemory(p)
sp.internal.priv = priv
default:
return errors.New("transport: invalid key algorithm; only RSA and ECDSA are supported")
}
return nil
}
// Certificate returns the associated certificate, or nil if
// one isn't ready.
func (sp *StandardProvider) Certificate() *x509.Certificate {
return sp.internal.cert
}
// CertificateRequest takes some metadata about a certificate request,
// and attempts to produce a certificate signing request suitable for
// sending to a certificate authority.
func (sp *StandardProvider) CertificateRequest(req *csr.CertificateRequest) ([]byte, error) {
if sp.internal.priv == nil {
if req.KeyRequest == nil {
return nil, errors.New("transport: invalid key request in csr.CertificateRequest")
}
sp.Generate(req.KeyRequest.Algo(), req.KeyRequest.Size())
}
return csr.Generate(sp.internal.priv, req)
}
// ErrCertificateUnavailable is returned when a key is available, but
// there is no accompanying certificate.
var ErrCertificateUnavailable = errors.New("transport: certificate unavailable")
// Load a private key and certificate from disk.
func (sp *StandardProvider) Load() (err error) {
if !sp.Persistent() {
return
}
var clearKey = true
defer func() {
if err != nil {
if clearKey {
sp.resetKey()
}
sp.resetCert()
}
}()
sp.internal.keyPEM, err = ioutil.ReadFile(sp.Paths.KeyFile)
if err != nil {
return
}
sp.internal.priv, err = helpers.ParsePrivateKeyPEM(sp.internal.keyPEM)
if err != nil {
return
}
clearKey = false
sp.internal.certPEM, err = ioutil.ReadFile(sp.Paths.CertFile)
if err != nil {
return ErrCertificateUnavailable
}
sp.internal.cert, err = helpers.ParseCertificatePEM(sp.internal.certPEM)
if err != nil {
err = errors.New("transport: invalid certificate")
return
}
p, _ := pem.Decode(sp.internal.keyPEM)
switch sp.internal.cert.PublicKey.(type) {
case *rsa.PublicKey:
if p.Type != "RSA PRIVATE KEY" {
err = errors.New("transport: PEM type " + p.Type + " is invalid for an RSA key")
return
}
case *ecdsa.PublicKey:
if p.Type != "EC PRIVATE KEY" {
err = errors.New("transport: PEM type " + p.Type + " is invalid for an ECDSA key")
return
}
default:
err = errors.New("transport: invalid public key type")
}
if err != nil {
clearKey = true
return
}
return nil
}
// Ready returns true if the provider has a key and certificate
// loaded. The certificate should be checked by the end user for
// validity.
func (sp *StandardProvider) Ready() bool {
switch {
case sp.internal.priv == nil:
return false
case sp.internal.cert == nil:
return false
case sp.internal.keyPEM == nil:
return false
case sp.internal.certPEM == nil:
return false
default:
return true
}
}
// SetCertificatePEM receives a PEM-encoded certificate and loads it
// into the provider.
func (sp *StandardProvider) SetCertificatePEM(certPEM []byte) error {
cert, err := helpers.ParseCertificatePEM(certPEM)
if err != nil {
return errors.New("transport: invalid certificate")
}
sp.internal.certPEM = certPEM
sp.internal.cert = cert
return nil
}
// SignalFailure is provided to implement the KeyProvider interface,
// and always returns false.
func (sp *StandardProvider) SignalFailure(err error) bool {
return false
}
// SignCSR takes a template certificate request and signs it.
func (sp *StandardProvider) SignCSR(tpl *x509.CertificateRequest) ([]byte, error) {
return x509.CreateCertificateRequest(rand.Reader, tpl, sp.internal.priv)
}
// Store writes the key and certificate to disk, if necessary.
func (sp *StandardProvider) Store() error {
if !sp.Ready() {
return errors.New("transport: provider does not have a key and certificate")
}
err := ioutil.WriteFile(sp.Paths.CertFile, sp.internal.certPEM, 0644)
if err != nil {
return err
}
return ioutil.WriteFile(sp.Paths.KeyFile, sp.internal.keyPEM, 0600)
}
// X509KeyPair returns a tls.Certificate for the provider.
func (sp *StandardProvider) X509KeyPair() (tls.Certificate, error) {
cert, err := tls.X509KeyPair(sp.internal.certPEM, sp.internal.keyPEM)
if err != nil {
return tls.Certificate{}, err
}
if cert.Leaf == nil {
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return tls.Certificate{}, err
}
}
return cert, nil
}