github.com/bestbeforetoday/fabric-ca@v2.0.0-alpha+incompatible/util/csp.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"encoding/hex"
    26  	"encoding/pem"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"strings"
    30  	_ "time" // for ocspSignerFromConfig
    31  
    32  	_ "github.com/cloudflare/cfssl/cli" // for ocspSignerFromConfig
    33  	"github.com/cloudflare/cfssl/config"
    34  	"github.com/cloudflare/cfssl/csr"
    35  	"github.com/cloudflare/cfssl/helpers"
    36  	"github.com/cloudflare/cfssl/log"
    37  	_ "github.com/cloudflare/cfssl/ocsp" // for ocspSignerFromConfig
    38  	"github.com/cloudflare/cfssl/signer"
    39  	"github.com/cloudflare/cfssl/signer/local"
    40  	"github.com/hyperledger/fabric/bccsp"
    41  	"github.com/hyperledger/fabric/bccsp/factory"
    42  	cspsigner "github.com/hyperledger/fabric/bccsp/signer"
    43  	"github.com/hyperledger/fabric/bccsp/utils"
    44  	"github.com/pkg/errors"
    45  )
    46  
    47  // GetDefaultBCCSP returns the default BCCSP
    48  func GetDefaultBCCSP() bccsp.BCCSP {
    49  	return factory.GetDefault()
    50  }
    51  
    52  // InitBCCSP initializes BCCSP
    53  func InitBCCSP(optsPtr **factory.FactoryOpts, mspDir, homeDir string) (bccsp.BCCSP, error) {
    54  	err := ConfigureBCCSP(optsPtr, mspDir, homeDir)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	csp, err := GetBCCSP(*optsPtr, homeDir)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return csp, nil
    63  }
    64  
    65  // GetBCCSP returns BCCSP
    66  func GetBCCSP(opts *factory.FactoryOpts, homeDir string) (bccsp.BCCSP, error) {
    67  
    68  	// Get BCCSP from the opts
    69  	csp, err := factory.GetBCCSPFromOpts(opts)
    70  	if err != nil {
    71  		return nil, errors.WithMessage(err, "Failed to get BCCSP with opts")
    72  	}
    73  	return csp, nil
    74  }
    75  
    76  // makeFileNamesAbsolute makes all relative file names associated with CSP absolute,
    77  // relative to 'homeDir'.
    78  func makeFileNamesAbsolute(opts *factory.FactoryOpts, homeDir string) error {
    79  	var err error
    80  	if opts != nil && opts.SwOpts != nil && opts.SwOpts.FileKeystore != nil {
    81  		fks := opts.SwOpts.FileKeystore
    82  		fks.KeyStorePath, err = MakeFileAbs(fks.KeyStorePath, homeDir)
    83  	}
    84  	return err
    85  }
    86  
    87  // BccspBackedSigner attempts to create a signer using csp bccsp.BCCSP. This csp could be SW (golang crypto)
    88  // PKCS11 or whatever BCCSP-conformant library is configured
    89  func BccspBackedSigner(caFile, keyFile string, policy *config.Signing, csp bccsp.BCCSP) (signer.Signer, error) {
    90  	_, cspSigner, parsedCa, err := GetSignerFromCertFile(caFile, csp)
    91  	if err != nil {
    92  		// Fallback: attempt to read out of keyFile and import
    93  		log.Debugf("No key found in BCCSP keystore, attempting fallback")
    94  		var key bccsp.Key
    95  		var signer crypto.Signer
    96  
    97  		key, err = ImportBCCSPKeyFromPEM(keyFile, csp, false)
    98  		if err != nil {
    99  			return nil, errors.WithMessage(err, fmt.Sprintf("Could not find the private key in BCCSP keystore nor in keyfile '%s'", keyFile))
   100  		}
   101  
   102  		signer, err = cspsigner.New(csp, key)
   103  		if err != nil {
   104  			return nil, errors.WithMessage(err, "Failed initializing CryptoSigner")
   105  		}
   106  		cspSigner = signer
   107  	}
   108  
   109  	signer, err := local.NewSigner(cspSigner, parsedCa, signer.DefaultSigAlgo(cspSigner), policy)
   110  	if err != nil {
   111  		return nil, errors.Wrap(err, "Failed to create new signer")
   112  	}
   113  	return signer, nil
   114  }
   115  
   116  // getBCCSPKeyOpts generates a key as specified in the request.
   117  // This supports ECDSA and RSA.
   118  func getBCCSPKeyOpts(kr csr.KeyRequest, ephemeral bool) (opts bccsp.KeyGenOpts, err error) {
   119  	if kr == nil {
   120  		return &bccsp.ECDSAKeyGenOpts{Temporary: ephemeral}, nil
   121  	}
   122  	log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size())
   123  	switch kr.Algo() {
   124  	case "rsa":
   125  		switch kr.Size() {
   126  		case 2048:
   127  			return &bccsp.RSA2048KeyGenOpts{Temporary: ephemeral}, nil
   128  		case 3072:
   129  			return &bccsp.RSA3072KeyGenOpts{Temporary: ephemeral}, nil
   130  		case 4096:
   131  			return &bccsp.RSA4096KeyGenOpts{Temporary: ephemeral}, nil
   132  		default:
   133  			// Need to add a way to specify arbitrary RSA key size to bccsp
   134  			return nil, errors.Errorf("Invalid RSA key size: %d", kr.Size())
   135  		}
   136  	case "ecdsa":
   137  		switch kr.Size() {
   138  		case 256:
   139  			return &bccsp.ECDSAP256KeyGenOpts{Temporary: ephemeral}, nil
   140  		case 384:
   141  			return &bccsp.ECDSAP384KeyGenOpts{Temporary: ephemeral}, nil
   142  		case 521:
   143  			// Need to add curve P521 to bccsp
   144  			// return &bccsp.ECDSAP512KeyGenOpts{Temporary: false}, nil
   145  			return nil, errors.New("Unsupported ECDSA key size: 521")
   146  		default:
   147  			return nil, errors.Errorf("Invalid ECDSA key size: %d", kr.Size())
   148  		}
   149  	default:
   150  		return nil, errors.Errorf("Invalid algorithm: %s", kr.Algo())
   151  	}
   152  }
   153  
   154  // GetSignerFromCert load private key represented by ski and return bccsp signer that conforms to crypto.Signer
   155  func GetSignerFromCert(cert *x509.Certificate, csp bccsp.BCCSP) (bccsp.Key, crypto.Signer, error) {
   156  	if csp == nil {
   157  		return nil, nil, errors.New("CSP was not initialized")
   158  	}
   159  	// get the public key in the right format
   160  	certPubK, err := csp.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
   161  	if err != nil {
   162  		return nil, nil, errors.WithMessage(err, "Failed to import certificate's public key")
   163  	}
   164  	// Get the key given the SKI value
   165  	ski := certPubK.SKI()
   166  	privateKey, err := csp.GetKey(ski)
   167  	if err != nil {
   168  		return nil, nil, errors.WithMessage(err, "Could not find matching private key for SKI")
   169  	}
   170  	// BCCSP returns a public key if the private key for the SKI wasn't found, so
   171  	// we need to return an error in that case.
   172  	if !privateKey.Private() {
   173  		return nil, nil, errors.Errorf("The private key associated with the certificate with SKI '%s' was not found", hex.EncodeToString(ski))
   174  	}
   175  	// Construct and initialize the signer
   176  	signer, err := cspsigner.New(csp, privateKey)
   177  	if err != nil {
   178  		return nil, nil, errors.WithMessage(err, "Failed to load ski from bccsp")
   179  	}
   180  	return privateKey, signer, nil
   181  }
   182  
   183  // GetSignerFromCertFile load skiFile and load private key represented by ski and return bccsp signer that conforms to crypto.Signer
   184  func GetSignerFromCertFile(certFile string, csp bccsp.BCCSP) (bccsp.Key, crypto.Signer, *x509.Certificate, error) {
   185  	// Load cert file
   186  	certBytes, err := ioutil.ReadFile(certFile)
   187  	if err != nil {
   188  		return nil, nil, nil, errors.Wrapf(err, "Could not read certFile '%s'", certFile)
   189  	}
   190  	// Parse certificate
   191  	parsedCa, err := helpers.ParseCertificatePEM(certBytes)
   192  	if err != nil {
   193  		return nil, nil, nil, err
   194  	}
   195  	// Get the signer from the cert
   196  	key, cspSigner, err := GetSignerFromCert(parsedCa, csp)
   197  	return key, cspSigner, parsedCa, err
   198  }
   199  
   200  // BCCSPKeyRequestGenerate generates keys through BCCSP
   201  // somewhat mirroring to cfssl/req.KeyRequest.Generate()
   202  func BCCSPKeyRequestGenerate(req *csr.CertificateRequest, myCSP bccsp.BCCSP) (bccsp.Key, crypto.Signer, error) {
   203  	log.Infof("generating key: %+v", req.KeyRequest)
   204  	keyOpts, err := getBCCSPKeyOpts(req.KeyRequest, false)
   205  	if err != nil {
   206  		return nil, nil, err
   207  	}
   208  	key, err := myCSP.KeyGen(keyOpts)
   209  	if err != nil {
   210  		return nil, nil, err
   211  	}
   212  	cspSigner, err := cspsigner.New(myCSP, key)
   213  	if err != nil {
   214  		return nil, nil, errors.WithMessage(err, "Failed initializing CryptoSigner")
   215  	}
   216  	return key, cspSigner, nil
   217  }
   218  
   219  // ImportBCCSPKeyFromPEM attempts to create a private BCCSP key from a pem file keyFile
   220  func ImportBCCSPKeyFromPEM(keyFile string, myCSP bccsp.BCCSP, temporary bool) (bccsp.Key, error) {
   221  	keyBuff, err := ioutil.ReadFile(keyFile)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	key, err := utils.PEMtoPrivateKey(keyBuff, nil)
   226  	if err != nil {
   227  		return nil, errors.WithMessage(err, fmt.Sprintf("Failed parsing private key from %s", keyFile))
   228  	}
   229  	switch key.(type) {
   230  	case *ecdsa.PrivateKey:
   231  		priv, err := utils.PrivateKeyToDER(key.(*ecdsa.PrivateKey))
   232  		if err != nil {
   233  			return nil, errors.WithMessage(err, fmt.Sprintf("Failed to convert ECDSA private key for '%s'", keyFile))
   234  		}
   235  		sk, err := myCSP.KeyImport(priv, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: temporary})
   236  		if err != nil {
   237  			return nil, errors.WithMessage(err, fmt.Sprintf("Failed to import ECDSA private key for '%s'", keyFile))
   238  		}
   239  		return sk, nil
   240  	case *rsa.PrivateKey:
   241  		return nil, errors.Errorf("Failed to import RSA key from %s; RSA private key import is not supported", keyFile)
   242  	default:
   243  		return nil, errors.Errorf("Failed to import key from %s: invalid secret key type", keyFile)
   244  	}
   245  }
   246  
   247  // LoadX509KeyPair reads and parses a public/private key pair from a pair
   248  // of files. The files must contain PEM encoded data. The certificate file
   249  // may contain intermediate certificates following the leaf certificate to
   250  // form a certificate chain. On successful return, Certificate.Leaf will
   251  // be nil because the parsed form of the certificate is not retained.
   252  //
   253  // This function originated from crypto/tls/tls.go and was adapted to use a
   254  // BCCSP Signer
   255  func LoadX509KeyPair(certFile, keyFile string, csp bccsp.BCCSP) (*tls.Certificate, error) {
   256  
   257  	certPEMBlock, err := ioutil.ReadFile(certFile)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	cert := &tls.Certificate{}
   263  	var skippedBlockTypes []string
   264  	for {
   265  		var certDERBlock *pem.Block
   266  		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
   267  		if certDERBlock == nil {
   268  			break
   269  		}
   270  		if certDERBlock.Type == "CERTIFICATE" {
   271  			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
   272  		} else {
   273  			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
   274  		}
   275  	}
   276  
   277  	if len(cert.Certificate) == 0 {
   278  		if len(skippedBlockTypes) == 0 {
   279  			return nil, errors.Errorf("Failed to find PEM block in file %s", certFile)
   280  		}
   281  		if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
   282  			return nil, errors.Errorf("Failed to find certificate PEM data in file %s, but did find a private key; PEM inputs may have been switched", certFile)
   283  		}
   284  		return nil, errors.Errorf("Failed to find \"CERTIFICATE\" PEM block in file %s after skipping PEM blocks of the following types: %v", certFile, skippedBlockTypes)
   285  	}
   286  
   287  	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	_, cert.PrivateKey, err = GetSignerFromCert(x509Cert, csp)
   293  	if err != nil {
   294  		if keyFile != "" {
   295  			log.Debugf("Could not load TLS certificate with BCCSP: %s", err)
   296  			log.Debugf("Attempting fallback with certfile %s and keyfile %s", certFile, keyFile)
   297  			fallbackCerts, err := tls.LoadX509KeyPair(certFile, keyFile)
   298  			if err != nil {
   299  				return nil, errors.Wrapf(err, "Could not get the private key %s that matches %s", keyFile, certFile)
   300  			}
   301  			cert = &fallbackCerts
   302  		} else {
   303  			return nil, errors.WithMessage(err, "Could not load TLS certificate with BCCSP")
   304  		}
   305  
   306  	}
   307  
   308  	return cert, nil
   309  }