forked from hyperledger/fabric-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
csp.go
244 lines (223 loc) · 8.68 KB
/
csp.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
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Notice: This file has been modified for Hyperledger Fabric SDK Go usage.
Please review third_party pinning scripts and patches for more details.
*/
package util
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io/ioutil"
"strings"
"github.com/pkg/errors"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
factory "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/sdkpatch/cryptosuitebridge"
log "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/sdkpatch/logbridge"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/core"
)
// getBCCSPKeyOpts generates a key as specified in the request.
// This supports ECDSA and RSA.
func getBCCSPKeyOpts(kr csr.KeyRequest, ephemeral bool) (opts core.KeyGenOpts, err error) {
if kr == nil {
return factory.GetECDSAKeyGenOpts(ephemeral), nil
}
log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size())
switch kr.Algo() {
case "rsa":
switch kr.Size() {
case 2048:
return factory.GetRSA2048KeyGenOpts(ephemeral), nil
case 3072:
return factory.GetRSA3072KeyGenOpts(ephemeral), nil
case 4096:
return factory.GetRSA4096KeyGenOpts(ephemeral), nil
default:
// Need to add a way to specify arbitrary RSA key size to bccsp
return nil, errors.Errorf("Invalid RSA key size: %d", kr.Size())
}
case "ecdsa":
switch kr.Size() {
case 256:
return factory.GetECDSAP256KeyGenOpts(ephemeral), nil
case 384:
return factory.GetECDSAP384KeyGenOpts(ephemeral), nil
case 521:
// Need to add curve P521 to bccsp
// return &bccsp.ECDSAP512KeyGenOpts{Temporary: false}, nil
return nil, errors.New("Unsupported ECDSA key size: 521")
default:
return nil, errors.Errorf("Invalid ECDSA key size: %d", kr.Size())
}
default:
return nil, errors.Errorf("Invalid algorithm: %s", kr.Algo())
}
}
// GetSignerFromCert load private key represented by ski and return bccsp signer that conforms to crypto.Signer
func GetSignerFromCert(cert *x509.Certificate, csp core.CryptoSuite) (core.Key, crypto.Signer, error) {
if csp == nil {
return nil, nil, errors.New("CSP was not initialized")
}
// get the public key in the right format
certPubK, err := csp.KeyImport(cert, factory.GetX509PublicKeyImportOpts(true))
if err != nil {
return nil, nil, errors.WithMessage(err, "Failed to import certificate's public key")
}
// Get the key given the SKI value
ski := certPubK.SKI()
privateKey, err := csp.GetKey(ski)
if err != nil {
return nil, nil, errors.WithMessage(err, "Could not find matching private key for SKI")
}
// BCCSP returns a public key if the private key for the SKI wasn't found, so
// we need to return an error in that case.
if !privateKey.Private() {
return nil, nil, errors.Errorf("The private key associated with the certificate with SKI '%s' was not found", hex.EncodeToString(ski))
}
// Construct and initialize the signer
signer, err := factory.NewCspSigner(csp, privateKey)
if err != nil {
return nil, nil, errors.WithMessage(err, "Failed to load ski from bccsp")
}
return privateKey, signer, nil
}
// GetSignerFromCertFile load skiFile and load private key represented by ski and return bccsp signer that conforms to crypto.Signer
func GetSignerFromCertFile(certFile string, csp core.CryptoSuite) (core.Key, crypto.Signer, *x509.Certificate, error) {
// Load cert file
certBytes, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "Could not read certFile '%s'", certFile)
}
// Parse certificate
parsedCa, err := helpers.ParseCertificatePEM(certBytes)
if err != nil {
return nil, nil, nil, err
}
// Get the signer from the cert
key, cspSigner, err := GetSignerFromCert(parsedCa, csp)
return key, cspSigner, parsedCa, err
}
// BCCSPKeyRequestGenerate generates keys through BCCSP
// somewhat mirroring to cfssl/req.KeyRequest.Generate()
func BCCSPKeyRequestGenerate(req *csr.CertificateRequest, myCSP core.CryptoSuite) (core.Key, crypto.Signer, error) {
log.Infof("generating key: %+v", req.KeyRequest)
keyOpts, err := getBCCSPKeyOpts(req.KeyRequest, false)
if err != nil {
return nil, nil, err
}
key, err := myCSP.KeyGen(keyOpts)
if err != nil {
return nil, nil, err
}
cspSigner, err := factory.NewCspSigner(myCSP, key)
if err != nil {
return nil, nil, errors.WithMessage(err, "Failed initializing CryptoSigner")
}
return key, cspSigner, nil
}
// ImportBCCSPKeyFromPEM attempts to create a private BCCSP key from a pem file keyFile
func ImportBCCSPKeyFromPEM(keyFile string, myCSP core.CryptoSuite, temporary bool) (core.Key, error) {
keyBuff, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, err
}
key, err := ImportBCCSPKeyFromPEMBytes(keyBuff, myCSP, temporary)
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed parsing private key from key file %s", keyFile))
}
return key, nil
}
// ImportBCCSPKeyFromPEMBytes attempts to create a private BCCSP key from a pem byte slice
func ImportBCCSPKeyFromPEMBytes(keyBuff []byte, myCSP core.CryptoSuite, temporary bool) (core.Key, error) {
keyFile := "pem bytes"
key, err := factory.PEMtoPrivateKey(keyBuff, nil)
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed parsing private key from %s", keyFile))
}
switch key.(type) {
case *ecdsa.PrivateKey:
priv, err := factory.PrivateKeyToDER(key.(*ecdsa.PrivateKey))
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to convert ECDSA private key for '%s'", keyFile))
}
sk, err := myCSP.KeyImport(priv, factory.GetECDSAPrivateKeyImportOpts(temporary))
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to import ECDSA private key for '%s'", keyFile))
}
return sk, nil
case *rsa.PrivateKey:
return nil, errors.Errorf("Failed to import RSA key from %s; RSA private key import is not supported", keyFile)
default:
return nil, errors.Errorf("Failed to import key from %s: invalid secret key type", keyFile)
}
}
// LoadX509KeyPair reads and parses a public/private key pair from a pair
// of files. The files must contain PEM encoded data. The certificate file
// may contain intermediate certificates following the leaf certificate to
// form a certificate chain. On successful return, Certificate.Leaf will
// be nil because the parsed form of the certificate is not retained.
//
// This function originated from crypto/tls/tls.go and was adapted to use a
// BCCSP Signer
func LoadX509KeyPair(certFile, keyFile []byte, csp core.CryptoSuite) (*tls.Certificate, error) {
certPEMBlock := certFile
cert := &tls.Certificate{}
var skippedBlockTypes []string
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
} else {
skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
}
}
if len(cert.Certificate) == 0 {
if len(skippedBlockTypes) == 0 {
return nil, errors.New("Failed to find PEM block in bytes")
}
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
return nil, errors.New("Failed to find certificate PEM data in bytes, but did find a private key; PEM inputs may have been switched")
}
return nil, errors.Errorf("Failed to find \"CERTIFICATE\" PEM block in file %s after skipping PEM blocks of the following types: %v", certFile, skippedBlockTypes)
}
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, err
}
_, cert.PrivateKey, err = GetSignerFromCert(x509Cert, csp)
if err != nil {
if keyFile != nil {
log.Debugf("Could not load TLS certificate with BCCSP: %s", err)
log.Debug("Attempting fallback with provided certfile and keyfile")
fallbackCerts, err := tls.X509KeyPair(certFile, keyFile)
if err != nil {
return nil, errors.Wrap(err, "Could not get the private key that matches the provided cert")
}
cert = &fallbackCerts
} else {
return nil, errors.WithMessage(err, "Could not load TLS certificate with BCCSP")
}
}
return cert, nil
}