Skip to content

Commit 1b54dcf

Browse files
committed
[FAB-3763] Fixing Intermediate CA certs sanitization
This change-set addresses the following issue: Currently, certificates of intermediate CAs are sanitized in the same way root CA certificates are. Namely, by using as parent certificate the certificate itself. But intermediate CA certificate must have a parent and therefore the sanitation must happen with the respect to that certificate. Change-Id: I2f9c7d7eb9e4c1d3aab722ddac5ffdf8189fdf37 Signed-off-by: Angelo De Caro <[email protected]>
1 parent 132817b commit 1b54dcf

File tree

11 files changed

+171
-166
lines changed

11 files changed

+171
-166
lines changed

msp/identities.go

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type identity struct {
4848
func newIdentity(id *IdentityIdentifier, cert *x509.Certificate, pk bccsp.Key, msp *bccspmsp) (Identity, error) {
4949
mspLogger.Debugf("Creating identity instance for ID %s", id)
5050

51+
// Sanitize first the certificate
5152
cert, err := msp.sanitizeCert(cert)
5253
if err != nil {
5354
return nil, err

msp/msp_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
"github.com/golang/protobuf/proto"
2929
"github.com/hyperledger/fabric/bccsp"
30+
"github.com/hyperledger/fabric/bccsp/sw"
3031
"github.com/hyperledger/fabric/core/config"
3132
"github.com/hyperledger/fabric/protos/msp"
3233
"github.com/stretchr/testify/assert"
@@ -859,3 +860,21 @@ func getIdentity(t *testing.T, path string) Identity {
859860

860861
return id
861862
}
863+
864+
func getLocalMSP(t *testing.T, dir string) MSP {
865+
conf, err := GetLocalMspConfig(dir, nil, "DEFAULT")
866+
assert.NoError(t, err)
867+
868+
thisMSP, err := NewBccspMsp()
869+
assert.NoError(t, err)
870+
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
871+
assert.NoError(t, err)
872+
csp, err := sw.New(256, "SHA2", ks)
873+
assert.NoError(t, err)
874+
thisMSP.(*bccspmsp).bccsp = csp
875+
876+
err = thisMSP.Setup(conf)
877+
assert.NoError(t, err)
878+
879+
return thisMSP
880+
}

msp/mspimpl.go

+58-20
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,32 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
310310
if len(conf.RootCerts) == 0 {
311311
return errors.New("Expected at least one CA certificate")
312312
}
313+
314+
// pre-create the verify options with roots and intermediates
315+
// this is needed to make certificate sanitation working.
316+
msp.opts = &x509.VerifyOptions{
317+
Roots: x509.NewCertPool(),
318+
Intermediates: x509.NewCertPool(),
319+
}
320+
for _, v := range conf.RootCerts {
321+
cert, err := msp.getCertFromPem(v)
322+
if err != nil {
323+
return err
324+
}
325+
326+
msp.opts.Roots.AddCert(cert)
327+
}
328+
for _, v := range conf.IntermediateCerts {
329+
cert, err := msp.getCertFromPem(v)
330+
if err != nil {
331+
return err
332+
}
333+
334+
msp.opts.Intermediates.AddCert(cert)
335+
}
336+
337+
// Load root and intermediate CA identities
338+
// Recall that when an identity is created, its certificate gets sanitized
313339
msp.rootCerts = make([]Identity, len(conf.RootCerts))
314340
for i, trustedCert := range conf.RootCerts {
315341
id, _, err := msp.getIdentityFromConf(trustedCert)
@@ -331,18 +357,6 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
331357
msp.intermediateCerts[i] = id
332358
}
333359

334-
// pre-create the verify options with roots and intermediates
335-
msp.opts = &x509.VerifyOptions{
336-
Roots: x509.NewCertPool(),
337-
Intermediates: x509.NewCertPool(),
338-
}
339-
for _, v := range msp.rootCerts {
340-
msp.opts.Roots.AddCert(v.(*identity).cert)
341-
}
342-
for _, v := range msp.intermediateCerts {
343-
msp.opts.Intermediates.AddCert(v.(*identity).cert)
344-
}
345-
346360
// make and fill the set of admin certs (if present)
347361
msp.admins = make([]Identity, len(conf.Admins))
348362
for i, admCert := range conf.Admins {
@@ -712,26 +726,35 @@ func (msp *bccspmsp) getCertificationChainForBCCSPIdentity(id *identity) ([]*x50
712726
return msp.getValidationChain(id.cert)
713727
}
714728

715-
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
729+
func (msp *bccspmsp) getUniqueValidationChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
716730
// ask golang to validate the cert for us based on the options that we've built at setup time
717-
validationChain, err := cert.Verify(*(msp.opts))
731+
validationChains, err := cert.Verify(*(msp.opts))
718732
if err != nil {
719733
return nil, fmt.Errorf("The supplied identity is not valid, Verify() returned %s", err)
720734
}
721735

722736
// we only support a single validation chain;
723737
// if there's more than one then there might
724738
// be unclarity about who owns the identity
725-
if len(validationChain) != 1 {
726-
return nil, fmt.Errorf("This MSP only supports a single validation chain, got %d", len(validationChain))
739+
if len(validationChains) != 1 {
740+
return nil, fmt.Errorf("This MSP only supports a single validation chain, got %d", len(validationChains))
741+
}
742+
743+
return validationChains[0], nil
744+
}
745+
746+
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
747+
validationChain, err := msp.getUniqueValidationChain(cert)
748+
if err != nil {
749+
return nil, fmt.Errorf("Failed getting validation chain %s", err)
727750
}
728751

729752
// we expect a chain of length at least 2
730-
if len(validationChain[0]) < 2 {
753+
if len(validationChain) < 2 {
731754
return nil, fmt.Errorf("Expected a chain of length at least 2, got %d", len(validationChain))
732755
}
733756

734-
return validationChain[0], nil
757+
return validationChain, nil
735758
}
736759

737760
// getCertificationChainIdentifier returns the certification chain identifier of the passed identity within this msp.
@@ -838,14 +861,29 @@ func (msp *bccspmsp) setupOUs(conf m.FabricMSPConfig) error {
838861
return nil
839862
}
840863

864+
// sanitizeCert ensures that x509 certificates signed using ECDSA
865+
// do have signatures in Low-S. If this is not the case, the certificate
866+
// is regenerated to have a Low-S signature.
841867
func (msp *bccspmsp) sanitizeCert(cert *x509.Certificate) (*x509.Certificate, error) {
842868
if isECDSASignedCert(cert) {
843869
// Lookup for a parent certificate to perform the sanitization
844870
var parentCert *x509.Certificate
845871
if cert.IsCA {
846-
parentCert = cert
872+
// at this point, cert might be a root CA certificate
873+
// or an intermediate CA certificate
874+
chain, err := msp.getUniqueValidationChain(cert)
875+
if err != nil {
876+
return nil, err
877+
}
878+
if len(chain) == 1 {
879+
// cert is a root CA certificate
880+
parentCert = cert
881+
} else {
882+
// cert is an intermediate CA certificate
883+
parentCert = chain[1]
884+
}
847885
} else {
848-
chain, err := msp.getValidationChain(cert)
886+
chain, err := msp.getUniqueValidationChain(cert)
849887
if err != nil {
850888
return nil, err
851889
}

msp/mspwithintermediatecas_test.go

+14-117
Original file line numberDiff line numberDiff line change
@@ -19,99 +19,16 @@ package msp
1919
import (
2020
"testing"
2121

22-
"github.com/golang/protobuf/proto"
23-
"github.com/hyperledger/fabric/bccsp"
24-
"github.com/hyperledger/fabric/protos/msp"
2522
"github.com/stretchr/testify/assert"
2623
)
2724

28-
// the following strings contain the credentials for a test MSP setup that has
29-
// 1) a key and a signcert (used to populate the default signing identity);
30-
// signcert is not signed by a CA directly but by an intermediate CA
31-
// 2) intermediatecert is an intermediate CA, signed by the CA
32-
// 3) cacert is the CA that signed the intermediate
33-
const key = `-----BEGIN EC PRIVATE KEY-----
34-
MHcCAQEEII27gKS2mFIIGkyGFEvHyv1khaJHe+p+sDt0++JByCDToAoGCCqGSM49
35-
AwEHoUQDQgAEJUUpwMg/jQ+qpmkVewEvwTySl+XWbd4AXtb/0XsDqXNcyXl0DVgA
36-
gJNGnt5r+bvZdB8SOk1ySAEEsCQArkarMg==
37-
-----END EC PRIVATE KEY-----`
38-
39-
var signcert = `-----BEGIN CERTIFICATE-----
40-
MIIDAzCCAqigAwIBAgIBAjAKBggqhkjOPQQDAjBsMQswCQYDVQQGEwJHQjEQMA4G
41-
A1UECAwHRW5nbGFuZDEOMAwGA1UECgwFQmFyMTkxDjAMBgNVBAsMBUJhcjE5MQ4w
42-
DAYDVQQDDAVCYXIxOTEbMBkGCSqGSIb3DQEJARYMQmFyMTktY2xpZW50MB4XDTE3
43-
MDIwOTE2MDcxMFoXDTE4MDIxOTE2MDcxMFowfDELMAkGA1UEBhMCR0IxEDAOBgNV
44-
BAgMB0VuZ2xhbmQxEDAOBgNVBAcMB0lwc3dpY2gxDjAMBgNVBAoMBUJhcjE5MQ4w
45-
DAYDVQQLDAVCYXIxOTEOMAwGA1UEAwwFQmFyMTkxGTAXBgkqhkiG9w0BCQEWCkJh
46-
cjE5LXBlZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQlRSnAyD+ND6qmaRV7
47-
AS/BPJKX5dZt3gBe1v/RewOpc1zJeXQNWACAk0ae3mv5u9l0HxI6TXJIAQSwJACu
48-
Rqsyo4IBKTCCASUwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZI
49-
AYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAd
50-
BgNVHQ4EFgQUwHzbLJQMaWd1cpHdkSaEFxdKB1owgYsGA1UdIwSBgzCBgIAUYxFe
51-
+cXOD5iQ223bZNdOuKCRiTKhZaRjMGExCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdF
52-
bmdsYW5kMRAwDgYDVQQHDAdJcHN3aWNoMQ4wDAYDVQQKDAVCYXIxOTEOMAwGA1UE
53-
CwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5ggEBMA4GA1UdDwEB/wQEAwIFoDATBgNV
54-
HSUEDDAKBggrBgEFBQcDATAKBggqhkjOPQQDAgNJADBGAiEAuMq65lOaie4705Ol
55-
Ow52DjbaO2YuIxK2auBCqNIu0gECIQCDoKdUQ/sa+9Ah1mzneE6iz/f/YFVWo4EP
56-
HeamPGiDTQ==
57-
-----END CERTIFICATE-----`
58-
59-
var intermediatecert = `-----BEGIN CERTIFICATE-----
60-
MIICITCCAcigAwIBAgIBATAKBggqhkjOPQQDAjBhMQswCQYDVQQGEwJHQjEQMA4G
61-
A1UECAwHRW5nbGFuZDEQMA4GA1UEBwwHSXBzd2ljaDEOMAwGA1UECgwFQmFyMTkx
62-
DjAMBgNVBAsMBUJhcjE5MQ4wDAYDVQQDDAVCYXIxOTAeFw0xNzAyMDkxNTUyMDBa
63-
Fw0yNzAyMDcxNTUyMDBaMGwxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5k
64-
MQ4wDAYDVQQKDAVCYXIxOTEOMAwGA1UECwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5
65-
MRswGQYJKoZIhvcNAQkBFgxCYXIxOS1jbGllbnQwWTATBgcqhkjOPQIBBggqhkjO
66-
PQMBBwNCAAQBymfTx4GWt1lnTV4Xp3skM5LJpZ40HVhCDLfvfrD8/3WQLHaLc7XW
67-
KpphhXW8HYLyyjkEZVLsAFHkKjwmlcpzo2YwZDAdBgNVHQ4EFgQUYxFe+cXOD5iQ
68-
223bZNdOuKCRiTIwHwYDVR0jBBgwFoAU4UJ1xRnh6zeW2IKABUOjIt9Wk8gwEgYD
69-
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAw
70-
RAIgGUgzRtqWx98KkgKNDyeEmBmhpptW966iS7+c8ig4ksMCIEyzhATMpiI4pHzH
71-
xSwZMvo3y3wkMwgf/WrhwdCyZNku
72-
-----END CERTIFICATE-----`
73-
74-
var cacert = `-----BEGIN CERTIFICATE-----
75-
MIICHDCCAcKgAwIBAgIJAJ/qse7uYF0LMAoGCCqGSM49BAMCMGExCzAJBgNVBAYT
76-
AkdCMRAwDgYDVQQIDAdFbmdsYW5kMRAwDgYDVQQHDAdJcHN3aWNoMQ4wDAYDVQQK
77-
DAVCYXIxOTEOMAwGA1UECwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5MB4XDTE3MDIw
78-
OTE1MzE1MloXDTM3MDIwNDE1MzE1MlowYTELMAkGA1UEBhMCR0IxEDAOBgNVBAgM
79-
B0VuZ2xhbmQxEDAOBgNVBAcMB0lwc3dpY2gxDjAMBgNVBAoMBUJhcjE5MQ4wDAYD
80-
VQQLDAVCYXIxOTEOMAwGA1UEAwwFQmFyMTkwWTATBgcqhkjOPQIBBggqhkjOPQMB
81-
BwNCAAQcG4qwA7jeGzgkakV+IYyQH/GwgtOw6+Y3ZabCmw8dk0vrDwdZ7fEI9C10
82-
b9ckm9n4LvnooSxQEzfLDk9N+S7yo2MwYTAdBgNVHQ4EFgQU4UJ1xRnh6zeW2IKA
83-
BUOjIt9Wk8gwHwYDVR0jBBgwFoAU4UJ1xRnh6zeW2IKABUOjIt9Wk8gwDwYDVR0T
84-
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSAAwRQIgGvB0
85-
854QmGi1yG5wnWMiwzQxtcEhvCXbnCuiQvr5VrkCIQDoMooDC/WmhBwuCfo7iGDo
86-
AsFd44a8aa9yzABfALG2Gw==
87-
-----END CERTIFICATE-----`
88-
8925
func TestMSPWithIntermediateCAs(t *testing.T) {
90-
keyinfo := &msp.KeyInfo{KeyIdentifier: "PEER", KeyMaterial: []byte(key)}
91-
92-
sigid := &msp.SigningIdentityInfo{PublicSigner: []byte(signcert), PrivateSigner: keyinfo}
93-
94-
cryptoConfig := &msp.FabricCryptoConfig{
95-
SignatureHashFamily: bccsp.SHA2,
96-
IdentityIdentifierHashFunction: bccsp.SHA256,
97-
}
98-
99-
fmspconf := &msp.FabricMSPConfig{
100-
RootCerts: [][]byte{[]byte(cacert)},
101-
IntermediateCerts: [][]byte{[]byte(intermediatecert)},
102-
SigningIdentity: sigid,
103-
Name: "DEFAULT",
104-
CryptoConfig: cryptoConfig}
105-
106-
fmpsjs, _ := proto.Marshal(fmspconf)
107-
108-
mspconf := &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}
109-
110-
thisMSP, err := NewBccspMsp()
111-
assert.NoError(t, err)
112-
113-
err = thisMSP.Setup(mspconf)
114-
assert.NoError(t, err)
26+
// testdata/intermediate contains the credentials for a test MSP setup that has
27+
// 1) a key and a signcert (used to populate the default signing identity);
28+
// signcert is not signed by a CA directly but by an intermediate CA
29+
// 2) intermediatecert is an intermediate CA, signed by the CA
30+
// 3) cacert is the CA that signed the intermediate
31+
thisMSP := getLocalMSP(t, "testdata/intermediate")
11532

11633
// This MSP will trust any cert signed by the CA directly OR by the intermediate
11734

@@ -136,33 +53,13 @@ func TestMSPWithIntermediateCAs(t *testing.T) {
13653
}
13754

13855
func TestIntermediateCAIdentityValidity(t *testing.T) {
139-
keyinfo := &msp.KeyInfo{KeyIdentifier: "PEER", KeyMaterial: []byte(key)}
140-
141-
sigid := &msp.SigningIdentityInfo{PublicSigner: []byte(signcert), PrivateSigner: keyinfo}
142-
143-
cryptoConfig := &msp.FabricCryptoConfig{
144-
SignatureHashFamily: bccsp.SHA2,
145-
IdentityIdentifierHashFunction: bccsp.SHA256,
146-
}
147-
148-
fmspconf := &msp.FabricMSPConfig{
149-
RootCerts: [][]byte{[]byte(cacert)},
150-
IntermediateCerts: [][]byte{[]byte(intermediatecert)},
151-
SigningIdentity: sigid,
152-
Name: "DEFAULT",
153-
CryptoConfig: cryptoConfig}
154-
155-
fmpsjs, _ := proto.Marshal(fmspconf)
156-
157-
mspconf := &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}
158-
159-
thisMSP, err := NewBccspMsp()
160-
assert.NoError(t, err)
161-
162-
err = thisMSP.Setup(mspconf)
163-
assert.NoError(t, err)
164-
165-
id, _, err := thisMSP.(*bccspmsp).getIdentityFromConf([]byte(intermediatecert))
166-
assert.NoError(t, err)
56+
// testdata/intermediate contains the credentials for a test MSP setup that has
57+
// 1) a key and a signcert (used to populate the default signing identity);
58+
// signcert is not signed by a CA directly but by an intermediate CA
59+
// 2) intermediatecert is an intermediate CA, signed by the CA
60+
// 3) cacert is the CA that signed the intermediate
61+
thisMSP := getLocalMSP(t, "testdata/intermediate")
62+
63+
id := thisMSP.(*bccspmsp).intermediateCerts[0]
16764
assert.Error(t, id.Validate())
16865
}

msp/ouconfig_test.go

+1-13
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package msp
1818

1919
import (
20-
"fmt"
21-
"os"
2220
"testing"
2321

2422
"github.com/stretchr/testify/assert"
@@ -28,17 +26,7 @@ func TestBadConfigOU(t *testing.T) {
2826
// testdata/badconfigou:
2927
// the configuration is such that only identities
3028
// with OU=COP2 and signed by the root ca should be validated
31-
conf, err := GetLocalMspConfig("testdata/badconfigou", nil, "DEFAULT")
32-
if err != nil {
33-
fmt.Printf("Setup should have succeeded, got err %s instead", err)
34-
os.Exit(-1)
35-
}
36-
37-
thisMSP, err := NewBccspMsp()
38-
assert.NoError(t, err)
39-
40-
err = thisMSP.Setup(conf)
41-
assert.NoError(t, err)
29+
thisMSP := getLocalMSP(t, "testdata/badconfigou")
4230

4331
id, err := thisMSP.GetDefaultSigningIdentity()
4432
assert.NoError(t, err)

msp/revocation_test.go

+7-16
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,12 @@ import (
2323
"github.com/stretchr/testify/assert"
2424
)
2525

26-
func getRevocationMSP(t *testing.T) MSP {
26+
func TestRevocation(t *testing.T) {
2727
// testdata/revocation
2828
// 1) a key and a signcert (used to populate the default signing identity);
2929
// 2) cacert is the CA that signed the intermediate;
3030
// 3) a revocation list that revokes signcert
31-
conf, err := GetLocalMspConfig("testdata/revocation", nil, "DEFAULT")
32-
assert.NoError(t, err)
33-
34-
thisMSP, err := NewBccspMsp()
35-
assert.NoError(t, err)
36-
37-
err = thisMSP.Setup(conf)
38-
assert.NoError(t, err)
39-
40-
return thisMSP
41-
}
42-
43-
func TestRevocation(t *testing.T) {
44-
thisMSP := getRevocationMSP(t)
31+
thisMSP := getLocalMSP(t, "testdata/revocation")
4532

4633
id, err := thisMSP.GetDefaultSigningIdentity()
4734
assert.NoError(t, err)
@@ -52,7 +39,11 @@ func TestRevocation(t *testing.T) {
5239
}
5340

5441
func TestIdentityPolicyPrincipalAgainstRevokedIdentity(t *testing.T) {
55-
thisMSP := getRevocationMSP(t)
42+
// testdata/revocation
43+
// 1) a key and a signcert (used to populate the default signing identity);
44+
// 2) cacert is the CA that signed the intermediate;
45+
// 3) a revocation list that revokes signcert
46+
thisMSP := getLocalMSP(t, "testdata/revocation")
5647

5748
id, err := thisMSP.GetDefaultSigningIdentity()
5849
assert.NoError(t, err)

0 commit comments

Comments
 (0)