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 }