Skip to content

Commit add9ff2

Browse files
ale-linuxyacovm
authored andcommitted
[FAB-1994] use intermediate CAs to validate certs
This change-set modifies msp to use intermediate CAs to validate certificates. Change-Id: I3d0a5888af78085686eba01704369800857625a2 Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent 31b9c40 commit add9ff2

File tree

4 files changed

+248
-26
lines changed

4 files changed

+248
-26
lines changed

msp/configbuilder.go

+31-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
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+
117
package msp
218

319
import (
@@ -64,17 +80,19 @@ func getPemMaterialFromDir(dir string) ([][]byte, error) {
6480
}
6581

6682
const (
67-
cacerts = "cacerts"
68-
admincerts = "admincerts"
69-
signcerts = "signcerts"
70-
keystore = "keystore"
83+
cacerts = "cacerts"
84+
admincerts = "admincerts"
85+
signcerts = "signcerts"
86+
keystore = "keystore"
87+
intermediatecerts = "intermediatecerts"
7188
)
7289

7390
func GetLocalMspConfig(dir string) (*msp.MSPConfig, error) {
7491
cacertDir := filepath.Join(dir, cacerts)
7592
signcertDir := filepath.Join(dir, signcerts)
7693
admincertDir := filepath.Join(dir, admincerts)
7794
keystoreDir := filepath.Join(dir, keystore)
95+
intermediatecertsDir := filepath.Join(dir, intermediatecerts)
7896

7997
cacerts, err := getPemMaterialFromDir(cacertDir)
8098
if err != nil || len(cacerts) == 0 {
@@ -96,6 +114,9 @@ func GetLocalMspConfig(dir string) (*msp.MSPConfig, error) {
96114
return nil, fmt.Errorf("Could not load a valid signing key from directory %s, err %s", keystoreDir, err)
97115
}
98116

117+
intermediatecert, _ := getPemMaterialFromDir(intermediatecertsDir)
118+
// intermediate certs are not mandatory
119+
99120
// FIXME: for now we're making the following assumptions
100121
// 1) there is exactly one signing cert
101122
// 2) there is exactly one signing key
@@ -105,7 +126,12 @@ func GetLocalMspConfig(dir string) (*msp.MSPConfig, error) {
105126

106127
sigid := &msp.SigningIdentityInfo{PublicSigner: signcert[0], PrivateSigner: keyinfo}
107128

108-
fmspconf := &msp.FabricMSPConfig{Admins: admincert, RootCerts: cacerts, SigningIdentity: sigid, Name: "DEFAULT"}
129+
fmspconf := &msp.FabricMSPConfig{
130+
Admins: admincert,
131+
RootCerts: cacerts,
132+
IntermediateCerts: intermediatecert,
133+
SigningIdentity: sigid,
134+
Name: "DEFAULT"}
109135

110136
fmpsjs, _ := proto.Marshal(fmspconf)
111137

msp/msp_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
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+
117
package msp
218

319
import (

msp/mspimpl.go

+72-21
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,29 @@ package msp
1919
import (
2020
"crypto/x509"
2121
"fmt"
22-
"time"
2322

2423
"encoding/pem"
2524

2625
"bytes"
2726

27+
"errors"
28+
2829
"github.com/golang/protobuf/proto"
2930
"github.com/hyperledger/fabric/bccsp"
3031
"github.com/hyperledger/fabric/bccsp/signer"
3132
"github.com/hyperledger/fabric/bccsp/sw"
3233
"github.com/hyperledger/fabric/protos/common"
3334
m "github.com/hyperledger/fabric/protos/msp"
34-
"github.com/syndtr/goleveldb/leveldb/errors"
3535
)
3636

3737
// This is an instantiation of an MSP that
3838
// uses BCCSP for its cryptographic primitives.
3939
type bccspmsp struct {
40-
// list of certs we trust
41-
trustedCerts []Identity
40+
// list of CA certs we trust
41+
rootCerts []Identity
42+
43+
// list of intermediate certs we trust
44+
intermediateCerts []Identity
4245

4346
// list of signing identities
4447
signer SigningIdentity
@@ -51,6 +54,9 @@ type bccspmsp struct {
5154

5255
// the provider identifier for this MSP
5356
name string
57+
58+
// verification options for MSP members
59+
opts *x509.VerifyOptions
5460
}
5561

5662
// NewBccspMsp returns an MSP instance backed up by a BCCSP
@@ -153,26 +159,46 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
153159
msp.name = conf.Name
154160
mspLogger.Debugf("Setting up MSP instance %s", msp.name)
155161

156-
// make and fill the set of admin certs
157-
msp.admins = make([]Identity, len(conf.Admins))
158-
for i, admCert := range conf.Admins {
159-
id, err := msp.getIdentityFromConf(admCert)
160-
if err != nil {
161-
return err
162-
}
162+
// make and fill the set of admin certs (if present)
163+
if conf.Admins != nil {
164+
msp.admins = make([]Identity, len(conf.Admins))
165+
for i, admCert := range conf.Admins {
166+
id, err := msp.getIdentityFromConf(admCert)
167+
if err != nil {
168+
return err
169+
}
163170

164-
msp.admins[i] = id
171+
msp.admins[i] = id
172+
}
165173
}
166174

167-
// make and fill the set of CA certs
168-
msp.trustedCerts = make([]Identity, len(conf.RootCerts))
175+
// make and fill the set of CA certs - we expect them to be there
176+
if len(conf.RootCerts) == 0 {
177+
return errors.New("Expected at least one CA certificate")
178+
}
179+
msp.rootCerts = make([]Identity, len(conf.RootCerts))
169180
for i, trustedCert := range conf.RootCerts {
170181
id, err := msp.getIdentityFromConf(trustedCert)
171182
if err != nil {
172183
return err
173184
}
174185

175-
msp.trustedCerts[i] = id
186+
msp.rootCerts[i] = id
187+
}
188+
189+
// make and fill the set of intermediate certs (if present)
190+
if conf.IntermediateCerts != nil {
191+
msp.intermediateCerts = make([]Identity, len(conf.IntermediateCerts))
192+
for i, trustedCert := range conf.IntermediateCerts {
193+
id, err := msp.getIdentityFromConf(trustedCert)
194+
if err != nil {
195+
return err
196+
}
197+
198+
msp.intermediateCerts[i] = id
199+
}
200+
} else {
201+
msp.intermediateCerts = make([]Identity, 0)
176202
}
177203

178204
// setup the signer (if present)
@@ -185,6 +211,18 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
185211
msp.signer = sid
186212
}
187213

214+
// pre-create the verify options with roots and intermediates
215+
msp.opts = &x509.VerifyOptions{
216+
Roots: x509.NewCertPool(),
217+
Intermediates: x509.NewCertPool(),
218+
}
219+
for _, v := range msp.rootCerts {
220+
msp.opts.Roots.AddCert(v.(*identity).cert)
221+
}
222+
for _, v := range msp.intermediateCerts {
223+
msp.opts.Intermediates.AddCert(v.(*identity).cert)
224+
}
225+
188226
return nil
189227
}
190228

@@ -230,16 +268,29 @@ func (msp *bccspmsp) Validate(id Identity) error {
230268
// this is how I can validate it given the
231269
// root of trust this MSP has
232270
case *identity:
233-
opts := x509.VerifyOptions{
234-
Roots: x509.NewCertPool(),
235-
CurrentTime: time.Now(),
271+
// we expect to have a valid VerifyOptions instance
272+
if msp.opts == nil {
273+
return errors.New("Invalid msp instance")
236274
}
237275

238-
for _, v := range msp.trustedCerts {
239-
opts.Roots.AddCert(v.(*identity).cert)
276+
// CAs cannot be directly used as identities..
277+
if id.(*identity).cert.IsCA {
278+
return errors.New("A CA certificate cannot be used directly by this MSP")
240279
}
241280

242-
_, err := id.(*identity).cert.Verify(opts)
281+
// at this point we might want to perform some
282+
// more elaborate validation. We do not do this
283+
// yet because we do not want to impose any
284+
// constraints without knowing the exact requirements,
285+
// but we at least list the kind of extra validation that we might perform:
286+
// 1) we might only allow a single verification chain (e.g. we expect the
287+
// cert to be signed exactly only by the CA or only by the intermediate)
288+
// 2) we might want to let golang find any path, and then have a blacklist
289+
// of paths (e.g. it can be signed by CA -> iCA1 -> iCA2 and it can be
290+
// signed by CA but not by CA -> iCA1)
291+
292+
// ask golang to validate the cert for us based on the options that we've built at setup time
293+
_, err := id.(*identity).cert.Verify(*(msp.opts))
243294
if err != nil {
244295
return fmt.Errorf("The supplied identity is not valid, Verify() returned %s", err)
245296
} else {

msp/mspwithintermediatecas_test.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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 msp
18+
19+
import (
20+
"testing"
21+
22+
"github.com/golang/protobuf/proto"
23+
"github.com/hyperledger/fabric/protos/msp"
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
// the following strings contain the credentials for a test MSP setup that has
28+
// 1) a key and a signcert (used to populate the default signing identity);
29+
// signcert is not signed by a CA directly but by an intermediate CA
30+
// 2) intermediatecert is an intermediate CA, signed by the CA
31+
// 3) cacert is the CA that signed the intermediate
32+
const key = `-----BEGIN EC PRIVATE KEY-----
33+
MHcCAQEEII27gKS2mFIIGkyGFEvHyv1khaJHe+p+sDt0++JByCDToAoGCCqGSM49
34+
AwEHoUQDQgAEJUUpwMg/jQ+qpmkVewEvwTySl+XWbd4AXtb/0XsDqXNcyXl0DVgA
35+
gJNGnt5r+bvZdB8SOk1ySAEEsCQArkarMg==
36+
-----END EC PRIVATE KEY-----`
37+
38+
var signcert = `-----BEGIN CERTIFICATE-----
39+
MIIDAzCCAqigAwIBAgIBAjAKBggqhkjOPQQDAjBsMQswCQYDVQQGEwJHQjEQMA4G
40+
A1UECAwHRW5nbGFuZDEOMAwGA1UECgwFQmFyMTkxDjAMBgNVBAsMBUJhcjE5MQ4w
41+
DAYDVQQDDAVCYXIxOTEbMBkGCSqGSIb3DQEJARYMQmFyMTktY2xpZW50MB4XDTE3
42+
MDIwOTE2MDcxMFoXDTE4MDIxOTE2MDcxMFowfDELMAkGA1UEBhMCR0IxEDAOBgNV
43+
BAgMB0VuZ2xhbmQxEDAOBgNVBAcMB0lwc3dpY2gxDjAMBgNVBAoMBUJhcjE5MQ4w
44+
DAYDVQQLDAVCYXIxOTEOMAwGA1UEAwwFQmFyMTkxGTAXBgkqhkiG9w0BCQEWCkJh
45+
cjE5LXBlZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQlRSnAyD+ND6qmaRV7
46+
AS/BPJKX5dZt3gBe1v/RewOpc1zJeXQNWACAk0ae3mv5u9l0HxI6TXJIAQSwJACu
47+
Rqsyo4IBKTCCASUwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZI
48+
AYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAd
49+
BgNVHQ4EFgQUwHzbLJQMaWd1cpHdkSaEFxdKB1owgYsGA1UdIwSBgzCBgIAUYxFe
50+
+cXOD5iQ223bZNdOuKCRiTKhZaRjMGExCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdF
51+
bmdsYW5kMRAwDgYDVQQHDAdJcHN3aWNoMQ4wDAYDVQQKDAVCYXIxOTEOMAwGA1UE
52+
CwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5ggEBMA4GA1UdDwEB/wQEAwIFoDATBgNV
53+
HSUEDDAKBggrBgEFBQcDATAKBggqhkjOPQQDAgNJADBGAiEAuMq65lOaie4705Ol
54+
Ow52DjbaO2YuIxK2auBCqNIu0gECIQCDoKdUQ/sa+9Ah1mzneE6iz/f/YFVWo4EP
55+
HeamPGiDTQ==
56+
-----END CERTIFICATE-----`
57+
58+
var intermediatecert = `-----BEGIN CERTIFICATE-----
59+
MIICITCCAcigAwIBAgIBATAKBggqhkjOPQQDAjBhMQswCQYDVQQGEwJHQjEQMA4G
60+
A1UECAwHRW5nbGFuZDEQMA4GA1UEBwwHSXBzd2ljaDEOMAwGA1UECgwFQmFyMTkx
61+
DjAMBgNVBAsMBUJhcjE5MQ4wDAYDVQQDDAVCYXIxOTAeFw0xNzAyMDkxNTUyMDBa
62+
Fw0yNzAyMDcxNTUyMDBaMGwxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5k
63+
MQ4wDAYDVQQKDAVCYXIxOTEOMAwGA1UECwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5
64+
MRswGQYJKoZIhvcNAQkBFgxCYXIxOS1jbGllbnQwWTATBgcqhkjOPQIBBggqhkjO
65+
PQMBBwNCAAQBymfTx4GWt1lnTV4Xp3skM5LJpZ40HVhCDLfvfrD8/3WQLHaLc7XW
66+
KpphhXW8HYLyyjkEZVLsAFHkKjwmlcpzo2YwZDAdBgNVHQ4EFgQUYxFe+cXOD5iQ
67+
223bZNdOuKCRiTIwHwYDVR0jBBgwFoAU4UJ1xRnh6zeW2IKABUOjIt9Wk8gwEgYD
68+
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAw
69+
RAIgGUgzRtqWx98KkgKNDyeEmBmhpptW966iS7+c8ig4ksMCIEyzhATMpiI4pHzH
70+
xSwZMvo3y3wkMwgf/WrhwdCyZNku
71+
-----END CERTIFICATE-----`
72+
73+
var cacert = `-----BEGIN CERTIFICATE-----
74+
MIICHDCCAcKgAwIBAgIJAJ/qse7uYF0LMAoGCCqGSM49BAMCMGExCzAJBgNVBAYT
75+
AkdCMRAwDgYDVQQIDAdFbmdsYW5kMRAwDgYDVQQHDAdJcHN3aWNoMQ4wDAYDVQQK
76+
DAVCYXIxOTEOMAwGA1UECwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5MB4XDTE3MDIw
77+
OTE1MzE1MloXDTM3MDIwNDE1MzE1MlowYTELMAkGA1UEBhMCR0IxEDAOBgNVBAgM
78+
B0VuZ2xhbmQxEDAOBgNVBAcMB0lwc3dpY2gxDjAMBgNVBAoMBUJhcjE5MQ4wDAYD
79+
VQQLDAVCYXIxOTEOMAwGA1UEAwwFQmFyMTkwWTATBgcqhkjOPQIBBggqhkjOPQMB
80+
BwNCAAQcG4qwA7jeGzgkakV+IYyQH/GwgtOw6+Y3ZabCmw8dk0vrDwdZ7fEI9C10
81+
b9ckm9n4LvnooSxQEzfLDk9N+S7yo2MwYTAdBgNVHQ4EFgQU4UJ1xRnh6zeW2IKA
82+
BUOjIt9Wk8gwHwYDVR0jBBgwFoAU4UJ1xRnh6zeW2IKABUOjIt9Wk8gwDwYDVR0T
83+
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSAAwRQIgGvB0
84+
854QmGi1yG5wnWMiwzQxtcEhvCXbnCuiQvr5VrkCIQDoMooDC/WmhBwuCfo7iGDo
85+
AsFd44a8aa9yzABfALG2Gw==
86+
-----END CERTIFICATE-----`
87+
88+
func TestMSPWithIntermediateCAs(t *testing.T) {
89+
keyinfo := &msp.KeyInfo{KeyIdentifier: "PEER", KeyMaterial: []byte(key)}
90+
91+
sigid := &msp.SigningIdentityInfo{PublicSigner: []byte(signcert), PrivateSigner: keyinfo}
92+
93+
fmspconf := &msp.FabricMSPConfig{
94+
RootCerts: [][]byte{[]byte(cacert)},
95+
IntermediateCerts: [][]byte{[]byte(intermediatecert)},
96+
SigningIdentity: sigid,
97+
Name: "DEFAULT"}
98+
99+
fmpsjs, _ := proto.Marshal(fmspconf)
100+
101+
mspconf := &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}
102+
103+
thisMSP, err := NewBccspMsp()
104+
assert.NoError(t, err)
105+
106+
err = thisMSP.Setup(mspconf)
107+
assert.NoError(t, err)
108+
109+
// This MSP will trust any cert signed by the CA directly OR by the intermediate
110+
111+
id, err := thisMSP.GetDefaultSigningIdentity()
112+
assert.NoError(t, err)
113+
114+
// ensure that we validate correctly the identity
115+
err = thisMSP.Validate(id.GetPublicVersion())
116+
assert.NoError(t, err)
117+
118+
// ensure that validation of an identity of the MSP with intermediate CAs
119+
// fails with the local MSP
120+
err = localMsp.Validate(id.GetPublicVersion())
121+
assert.Error(t, err)
122+
123+
// ensure that validation of an identity of the local MSP
124+
// fails with the MSP with intermediate CAs
125+
localMSPID, err := localMsp.GetDefaultSigningIdentity()
126+
assert.NoError(t, err)
127+
err = thisMSP.Validate(localMSPID.GetPublicVersion())
128+
assert.Error(t, err)
129+
}

0 commit comments

Comments
 (0)