Skip to content

Commit c6f9e02

Browse files
committed
[FAB-10396] Move chaincode tls keygen to core/common
This change set moves the TLS certificate generation found in core/chaincode/accesscontrol/ to core/common/tlsgen In order to be reused in the discovery CLI Change-Id: Icee57f00e09c85ac7ed717d7dafe744ea65258b1 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent 6eb36d3 commit c6f9e02

13 files changed

+135
-105
lines changed

core/chaincode/accesscontrol/ca.go common/crypto/tlsgen/ca.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ Copyright IBM Corp. All Rights Reserved.
44
SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
package accesscontrol
7+
package tlsgen
8+
9+
import (
10+
"crypto"
11+
"crypto/x509"
12+
)
813

914
// CertKeyPair denotes a TLS certificate and corresponding key,
1015
// both PEM encoded
@@ -13,6 +18,9 @@ type CertKeyPair struct {
1318
Cert []byte
1419
// Key is the key corresponding to the certificate, PEM encoded
1520
Key []byte
21+
22+
crypto.Signer
23+
TLSCert *x509.Certificate
1624
}
1725

1826
// CA defines a certificate authority that can generate
@@ -24,7 +32,7 @@ type CA interface {
2432
// newCertKeyPair returns a certificate and private key pair and nil,
2533
// or nil, error in case of failure
2634
// The certificate is signed by the CA and is used for TLS client authentication
27-
newClientCertKeyPair() (*certKeyPair, error)
35+
NewClientCertKeyPair() (*CertKeyPair, error)
2836

2937
// NewServerCertKeyPair returns a CertKeyPair and nil,
3038
// with a given custom SAN.
@@ -34,7 +42,7 @@ type CA interface {
3442
}
3543

3644
type ca struct {
37-
caCert *certKeyPair
45+
caCert *CertKeyPair
3846
}
3947

4048
func NewCA() (CA, error) {
@@ -55,17 +63,17 @@ func (c *ca) CertBytes() []byte {
5563
// newClientCertKeyPair returns a certificate and private key pair and nil,
5664
// or nil, error in case of failure
5765
// The certificate is signed by the CA and is used as a client TLS certificate
58-
func (c *ca) newClientCertKeyPair() (*certKeyPair, error) {
59-
return newCertKeyPair(false, false, "", c.caCert.Signer, c.caCert.cert)
66+
func (c *ca) NewClientCertKeyPair() (*CertKeyPair, error) {
67+
return newCertKeyPair(false, false, "", c.caCert.Signer, c.caCert.TLSCert)
6068
}
6169

6270
// newServerCertKeyPair returns a certificate and private key pair and nil,
6371
// or nil, error in case of failure
6472
// The certificate is signed by the CA and is used as a server TLS certificate
6573
func (c *ca) NewServerCertKeyPair(host string) (*CertKeyPair, error) {
66-
keypair, err := newCertKeyPair(false, true, host, c.caCert.Signer, c.caCert.cert)
74+
keypair, err := newCertKeyPair(false, true, host, c.caCert.Signer, c.caCert.TLSCert)
6775
if err != nil {
6876
return nil, err
6977
}
70-
return keypair.CertKeyPair, nil
78+
return keypair, nil
7179
}

core/chaincode/accesscontrol/ca_test.go common/crypto/tlsgen/ca_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
44
SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
package accesscontrol
7+
package tlsgen
88

99
import (
1010
"crypto/tls"
@@ -54,10 +54,10 @@ func TestTLSCA(t *testing.T) {
5454
defer srv.Stop()
5555
defer l.Close()
5656

57-
probeTLS := func(kp *certKeyPair) error {
58-
keyBytes, err := base64.StdEncoding.DecodeString(kp.privKeyString())
57+
probeTLS := func(kp *CertKeyPair) error {
58+
keyBytes, err := base64.StdEncoding.DecodeString(kp.PrivKeyString())
5959
assert.NoError(t, err)
60-
certBytes, err := base64.StdEncoding.DecodeString(kp.pubKeyString())
60+
certBytes, err := base64.StdEncoding.DecodeString(kp.PubKeyString())
6161
assert.NoError(t, err)
6262
cert, err := tls.X509KeyPair(certBytes, keyBytes)
6363
tlsCfg := &tls.Config{
@@ -78,14 +78,14 @@ func TestTLSCA(t *testing.T) {
7878

7979
// Good path - use a cert key pair generated from the CA
8080
// that the TLS server started with
81-
kp, err := ca.newClientCertKeyPair()
81+
kp, err := ca.NewClientCertKeyPair()
8282
assert.NoError(t, err)
8383
err = probeTLS(kp)
8484
assert.NoError(t, err)
8585

8686
// Bad path - use a cert key pair generated from a foreign CA
8787
foreignCA, _ := NewCA()
88-
kp, err = foreignCA.newClientCertKeyPair()
88+
kp, err = foreignCA.NewClientCertKeyPair()
8989
assert.NoError(t, err)
9090
err = probeTLS(kp)
9191
assert.Error(t, err)

core/chaincode/accesscontrol/key.go common/crypto/tlsgen/key.go

+29-21
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
44
SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
package accesscontrol
7+
package tlsgen
88

99
import (
1010
"crypto"
@@ -20,19 +20,11 @@ import (
2020
"time"
2121
)
2222

23-
type KeyGenFunc func() (*certKeyPair, error)
24-
25-
type certKeyPair struct {
26-
*CertKeyPair
27-
crypto.Signer
28-
cert *x509.Certificate
29-
}
30-
31-
func (p *certKeyPair) privKeyString() string {
23+
func (p *CertKeyPair) PrivKeyString() string {
3224
return base64.StdEncoding.EncodeToString(p.Key)
3325
}
3426

35-
func (p *certKeyPair) pubKeyString() string {
27+
func (p *CertKeyPair) PubKeyString() string {
3628
return base64.StdEncoding.EncodeToString(p.Cert)
3729
}
3830

@@ -62,7 +54,7 @@ func newCertTemplate() (x509.Certificate, error) {
6254
}, nil
6355
}
6456

65-
func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Signer, parent *x509.Certificate) (*certKeyPair, error) {
57+
func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Signer, parent *x509.Certificate) (*CertKeyPair, error) {
6658
privateKey, privBytes, err := newPrivKey()
6759
if err != nil {
6860
return nil, err
@@ -87,10 +79,8 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig
8779
template.NotAfter = tenYearsFromNow
8880
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
8981
if ip := net.ParseIP(host); ip != nil {
90-
logger.Debug("Classified", host, "as an IP address, adding it as an IP SAN")
9182
template.IPAddresses = append(template.IPAddresses, ip)
9283
} else {
93-
logger.Debug("Classified", host, "as a hostname, adding it as a DNS SAN")
9484
template.DNSNames = append(template.DNSNames, host)
9585
}
9686
}
@@ -111,12 +101,30 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig
111101
return nil, err
112102
}
113103
privKey := encodePEM("EC PRIVATE KEY", privBytes)
114-
return &certKeyPair{
115-
CertKeyPair: &CertKeyPair{
116-
Key: privKey,
117-
Cert: pubKey,
118-
},
119-
Signer: privateKey,
120-
cert: cert,
104+
return &CertKeyPair{
105+
Key: privKey,
106+
Cert: pubKey,
107+
Signer: privateKey,
108+
TLSCert: cert,
109+
}, nil
110+
}
111+
112+
func encodePEM(keyType string, data []byte) []byte {
113+
return pem.EncodeToMemory(&pem.Block{Type: keyType, Bytes: data})
114+
}
115+
116+
// CertKeyPairFromString converts the given strings in base64 encoding to a CertKeyPair
117+
func CertKeyPairFromString(privKey string, pubKey string) (*CertKeyPair, error) {
118+
priv, err := base64.StdEncoding.DecodeString(privKey)
119+
if err != nil {
120+
return nil, err
121+
}
122+
pub, err := base64.StdEncoding.DecodeString(pubKey)
123+
if err != nil {
124+
return nil, err
125+
}
126+
return &CertKeyPair{
127+
Key: priv,
128+
Cert: pub,
121129
}, nil
122130
}

common/crypto/tlsgen/key_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package tlsgen
8+
9+
import (
10+
"crypto/tls"
11+
"crypto/x509"
12+
"encoding/pem"
13+
"testing"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestCertEncoding(t *testing.T) {
19+
pair, err := newCertKeyPair(false, false, "", nil, nil)
20+
assert.NoError(t, err)
21+
assert.NotNil(t, pair)
22+
assert.NotEmpty(t, pair.PrivKeyString())
23+
assert.NotEmpty(t, pair.PubKeyString())
24+
pair2, err := CertKeyPairFromString(pair.PrivKeyString(), pair.PubKeyString())
25+
assert.Equal(t, pair.Key, pair2.Key)
26+
assert.Equal(t, pair.Cert, pair2.Cert)
27+
}
28+
29+
func TestLoadCert(t *testing.T) {
30+
pair, err := newCertKeyPair(false, false, "", nil, nil)
31+
assert.NoError(t, err)
32+
assert.NotNil(t, pair)
33+
tlsCertPair, err := tls.X509KeyPair(pair.Cert, pair.Key)
34+
assert.NoError(t, err)
35+
assert.NotNil(t, tlsCertPair)
36+
block, _ := pem.Decode(pair.Cert)
37+
cert, err := x509.ParseCertificate(block.Bytes)
38+
assert.NoError(t, err)
39+
assert.NotNil(t, cert)
40+
}

core/chaincode/accesscontrol/access.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212

1313
"github.com/golang/protobuf/proto"
14+
"github.com/hyperledger/fabric/common/crypto/tlsgen"
1415
"github.com/hyperledger/fabric/common/flogging"
1516
pb "github.com/hyperledger/fabric/protos/peer"
1617
"google.golang.org/grpc"
@@ -36,9 +37,9 @@ func (auth *Authenticator) Wrap(srv pb.ChaincodeSupportServer) pb.ChaincodeSuppo
3637
}
3738

3839
// NewAuthenticator returns a new authenticator that can wrap a chaincode service
39-
func NewAuthenticator(ca CA) *Authenticator {
40+
func NewAuthenticator(ca tlsgen.CA) *Authenticator {
4041
return &Authenticator{
41-
mapper: newCertMapper(ca.newClientCertKeyPair),
42+
mapper: newCertMapper(ca.NewClientCertKeyPair),
4243
}
4344
}
4445

@@ -51,8 +52,8 @@ func (ac *Authenticator) Generate(ccName string) (*CertAndPrivKeyPair, error) {
5152
return nil, err
5253
}
5354
return &CertAndPrivKeyPair{
54-
Key: cert.privKeyString(),
55-
Cert: cert.pubKeyString(),
55+
Key: cert.PrivKeyString(),
56+
Cert: cert.PubKeyString(),
5657
}, nil
5758
}
5859

core/chaincode/accesscontrol/access_test.go

+19-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"time"
1717

1818
"github.com/golang/protobuf/proto"
19+
"github.com/hyperledger/fabric/common/crypto/tlsgen"
1920
"github.com/hyperledger/fabric/common/flogging"
2021
pb "github.com/hyperledger/fabric/protos/peer"
2122
"github.com/op/go-logging"
@@ -66,7 +67,20 @@ func (cs *ccSrv) stop() {
6667
cs.l.Close()
6768
}
6869

69-
func newCCServer(t *testing.T, port int, expectedCCname string, withTLS bool, ca CA) *ccSrv {
70+
func createTLSService(t *testing.T, ca tlsgen.CA, host string) *grpc.Server {
71+
keyPair, err := ca.NewServerCertKeyPair(host)
72+
cert, err := tls.X509KeyPair(keyPair.Cert, keyPair.Key)
73+
assert.NoError(t, err)
74+
tlsConf := &tls.Config{
75+
Certificates: []tls.Certificate{cert},
76+
ClientAuth: tls.RequireAndVerifyClientCert,
77+
ClientCAs: x509.NewCertPool(),
78+
}
79+
tlsConf.ClientCAs.AppendCertsFromPEM(ca.CertBytes())
80+
return grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConf)))
81+
}
82+
83+
func newCCServer(t *testing.T, port int, expectedCCname string, withTLS bool, ca tlsgen.CA) *ccSrv {
7084
var s *grpc.Server
7185
if withTLS {
7286
s = createTLSService(t, ca, "localhost")
@@ -162,7 +176,7 @@ func TestAccessControl(t *testing.T) {
162176
Type: pb.ChaincodeMessage_PUT_STATE,
163177
}
164178

165-
ca, _ := NewCA()
179+
ca, _ := tlsgen.NewCA()
166180
srv := newCCServer(t, 7052, "example02", true, ca)
167181
auth := NewAuthenticator(ca)
168182
pb.RegisterChaincodeSupportServer(srv.grpcSrv, auth.Wrap(srv))
@@ -175,8 +189,8 @@ func TestAccessControl(t *testing.T) {
175189
assert.Contains(t, err.Error(), "context deadline exceeded")
176190

177191
// Create an attacker with its own TLS certificate
178-
maliciousCA, _ := NewCA()
179-
keyPair, err := maliciousCA.newClientCertKeyPair()
192+
maliciousCA, _ := tlsgen.NewCA()
193+
keyPair, err := maliciousCA.NewClientCertKeyPair()
180194
cert, err := tls.X509KeyPair(keyPair.Cert, keyPair.Key)
181195
assert.NoError(t, err)
182196
_, err = newClient(t, 7052, &cert, ca.CertBytes())
@@ -269,7 +283,7 @@ func TestAccessControl(t *testing.T) {
269283
// Create a real chaincode, that its cert was generated by us
270284
// but have it reconnect only after too much time.
271285
// This tests a use case where the CC's cert has been expired
272-
// and the CC has been compromized. We don't want it to be able
286+
// and the CC has been compromised. We don't want it to be able
273287
// to reconnect to us.
274288
kp, err = auth.Generate("example02")
275289
assert.NoError(t, err)

core/chaincode/accesscontrol/mapper.go

+5-25
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ SPDX-License-Identifier: Apache-2.0
77
package accesscontrol
88

99
import (
10-
"encoding/base64"
11-
"encoding/pem"
1210
"sync"
1311
"time"
1412

13+
"github.com/hyperledger/fabric/common/crypto/tlsgen"
1514
"github.com/hyperledger/fabric/common/util"
1615
"golang.org/x/net/context"
1716
"google.golang.org/grpc/credentials"
@@ -22,6 +21,8 @@ var ttl = time.Minute * 10
2221

2322
type certHash string
2423

24+
type KeyGenFunc func() (*tlsgen.CertKeyPair, error)
25+
2526
type certMapper struct {
2627
keyGen KeyGenFunc
2728
sync.RWMutex
@@ -56,37 +57,16 @@ func (r *certMapper) purge(hash certHash) {
5657
delete(r.m, hash)
5758
}
5859

59-
func certKeyPairFromString(privKey string, pubKey string) (*certKeyPair, error) {
60-
priv, err := base64.StdEncoding.DecodeString(privKey)
61-
if err != nil {
62-
return nil, err
63-
}
64-
pub, err := base64.StdEncoding.DecodeString(pubKey)
65-
if err != nil {
66-
return nil, err
67-
}
68-
return &certKeyPair{
69-
CertKeyPair: &CertKeyPair{
70-
Key: priv,
71-
Cert: pub,
72-
},
73-
}, nil
74-
}
75-
76-
func (r *certMapper) genCert(name string) (*certKeyPair, error) {
60+
func (r *certMapper) genCert(name string) (*tlsgen.CertKeyPair, error) {
7761
keyPair, err := r.keyGen()
7862
if err != nil {
7963
return nil, err
8064
}
81-
hash := util.ComputeSHA256(keyPair.cert.Raw)
65+
hash := util.ComputeSHA256(keyPair.TLSCert.Raw)
8266
r.register(certHash(hash), name)
8367
return keyPair, nil
8468
}
8569

86-
func encodePEM(keyType string, data []byte) []byte {
87-
return pem.EncodeToMemory(&pem.Block{Type: keyType, Bytes: data})
88-
}
89-
9070
// ExtractCertificateHash extracts the hash of the certificate from the stream
9171
func extractCertificateHashFromContext(ctx context.Context) []byte {
9272
pr, extracted := peer.FromContext(ctx)

0 commit comments

Comments
 (0)