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
|
// 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) {
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
}
|