Skip to content

Commit c8731ae

Browse files
committed
[FAB-4003] OU certificates fail to match
When checking the OU certificate against the intermediates and roots, the OU certificate is extracted using x509.ParseCertificate whereas the intermediates and roots are first sanitised then reimported through msp.bccsp.KeyImport. The reimporting has the potential to change the signature algorithm which in turn results in certificates not matching, even though they are the same. There were also inconsistencies with how intermediate chains were being validated. Some functions returned only the parent certificate. When an OU identifiers configuration referenced an intermediate certificate, chain validation would fail, as the root certificate was assumed to be the signing parent. Change-Id: If866cb4972e46a7fdb82b64947a997f563c242fe Signed-off-by: Nick Murray <[email protected]>
1 parent eaf7f4d commit c8731ae

File tree

10 files changed

+167
-11
lines changed

10 files changed

+167
-11
lines changed

msp/msp_test.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ func TestSignAndVerify(t *testing.T) {
411411
func TestSignAndVerifyFailures(t *testing.T) {
412412
msg := []byte("foo")
413413

414-
id, err := localMsp.GetDefaultSigningIdentity()
414+
id, err := localMspBad.GetDefaultSigningIdentity()
415415
if err != nil {
416416
t.Fatalf("GetSigningIdentity should have succeeded")
417417
return
@@ -512,7 +512,7 @@ func TestGetOU(t *testing.T) {
512512
}
513513

514514
func TestGetOUFail(t *testing.T) {
515-
id, err := localMsp.GetDefaultSigningIdentity()
515+
id, err := localMspBad.GetDefaultSigningIdentity()
516516
if err != nil {
517517
t.Fatalf("GetSigningIdentity should have succeeded")
518518
return
@@ -836,6 +836,9 @@ func TestIdentityPolicyPrincipalFails(t *testing.T) {
836836

837837
var conf *msp.MSPConfig
838838
var localMsp MSP
839+
840+
// Required because deleting the cert or msp options from localMsp causes parallel tests to fail
841+
var localMspBad MSP
839842
var mspMgr MSPManager
840843

841844
func TestMain(m *testing.M) {
@@ -858,12 +861,24 @@ func TestMain(m *testing.M) {
858861
os.Exit(-1)
859862
}
860863

864+
localMspBad, err = NewBccspMsp()
865+
if err != nil {
866+
fmt.Printf("Constructor for msp should have succeeded, got err %s instead", err)
867+
os.Exit(-1)
868+
}
869+
861870
err = localMsp.Setup(conf)
862871
if err != nil {
863872
fmt.Printf("Setup for msp should have succeeded, got err %s instead", err)
864873
os.Exit(-1)
865874
}
866875

876+
err = localMspBad.Setup(conf)
877+
if err != nil {
878+
fmt.Printf("Setup for msp should have succeeded, got err %s instead", err)
879+
os.Exit(-1)
880+
}
881+
867882
mspMgr = NewMSPManager()
868883
err = mspMgr.Setup([]MSP{localMsp})
869884
if err != nil {

msp/mspimpl.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,14 @@ func (msp *bccspmsp) getCertificationChainForBCCSPIdentity(id *identity) ([]*x50
680680
return nil, errors.New("A CA certificate cannot be used directly by this MSP")
681681
}
682682

683-
return msp.getValidationChain(id.cert)
683+
return msp.getValidationChain(id.cert, false)
684684
}
685685

686686
func (msp *bccspmsp) getUniqueValidationChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
687687
// ask golang to validate the cert for us based on the options that we've built at setup time
688+
if msp.opts == nil {
689+
return nil, fmt.Errorf("The supplied identity has no verify options")
690+
}
688691
validationChains, err := cert.Verify(*(msp.opts))
689692
if err != nil {
690693
return nil, fmt.Errorf("The supplied identity is not valid, Verify() returned %s", err)
@@ -700,7 +703,7 @@ func (msp *bccspmsp) getUniqueValidationChain(cert *x509.Certificate) ([]*x509.C
700703
return validationChains[0], nil
701704
}
702705

703-
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate) ([]*x509.Certificate, error) {
706+
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate, isIntermediateChain bool) ([]*x509.Certificate, error) {
704707
validationChain, err := msp.getUniqueValidationChain(cert)
705708
if err != nil {
706709
return nil, fmt.Errorf("Failed getting validation chain %s", err)
@@ -712,10 +715,14 @@ func (msp *bccspmsp) getValidationChain(cert *x509.Certificate) ([]*x509.Certifi
712715
}
713716

714717
// check that the parent is a leaf of the certification tree
715-
if msp.certificationTreeInternalNodesMap[string(validationChain[1].Raw)] {
718+
// if validating an intermediate chain, the first certificate will the parent
719+
parentPosition := 1
720+
if isIntermediateChain {
721+
parentPosition = 0
722+
}
723+
if msp.certificationTreeInternalNodesMap[string(validationChain[parentPosition].Raw)] {
716724
return nil, fmt.Errorf("Invalid validation chain. Parent certificate should be a leaf of the certification tree [%v].", cert.Raw)
717725
}
718-
719726
return validationChain, nil
720727
}
721728

@@ -753,14 +760,21 @@ func (msp *bccspmsp) getCertificationChainIdentifierFromChain(chain []*x509.Cert
753760
func (msp *bccspmsp) setupOUs(conf m.FabricMSPConfig) error {
754761
msp.ouIdentifiers = make(map[string][][]byte)
755762
for _, ou := range conf.OrganizationalUnitIdentifiers {
756-
// 1. check that it registered in msp.rootCerts or msp.intermediateCerts
763+
764+
// 1. check that certificate is registered in msp.rootCerts or msp.intermediateCerts
757765
cert, err := msp.getCertFromPem(ou.Certificate)
758766
if err != nil {
759767
return fmt.Errorf("Failed getting certificate for [%v]: [%s]", ou, err)
760768
}
761769

770+
// 2. Sanitize it to ensure like for like comparison
771+
cert, err = msp.sanitizeCert(cert)
772+
if err != nil {
773+
return fmt.Errorf("sanitizeCert failed %s", err)
774+
}
775+
762776
found := false
763-
root := true
777+
root := false
764778
// Search among root certificates
765779
for _, v := range msp.rootCerts {
766780
if v.(*identity).cert.Equal(cert) {
@@ -783,19 +797,19 @@ func (msp *bccspmsp) setupOUs(conf m.FabricMSPConfig) error {
783797
return fmt.Errorf("Failed adding OU. Certificate [%v] not in root or intermediate certs.", ou.Certificate)
784798
}
785799

786-
// 2. get the certification path for it
800+
// 3. get the certification path for it
787801
var certifiersIdentitifer []byte
788802
var chain []*x509.Certificate
789803
if root {
790804
chain = []*x509.Certificate{cert}
791805
} else {
792-
chain, err = msp.getValidationChain(cert)
806+
chain, err = msp.getValidationChain(cert, true)
793807
if err != nil {
794808
return fmt.Errorf("Failed computing validation chain for [%v]. [%s]", cert, err)
795809
}
796810
}
797811

798-
// 3. compute the hash of the certification path
812+
// 4. compute the hash of the certification path
799813
certifiersIdentitifer, err = msp.getCertificationChainIdentifierFromChain(chain)
800814
if err != nil {
801815
return fmt.Errorf("Failed computing Certifiers Identifier for [%v]. [%s]", ou.Certificate, err)
@@ -969,6 +983,9 @@ func (msp *bccspmsp) validateIdentityOUs(id *identity) error {
969983
}
970984

971985
if !found {
986+
if len(id.GetOrganizationalUnits()) == 0 {
987+
return fmt.Errorf("The identity certificate does not contain an Organizational Unit (OU)")
988+
}
972989
return fmt.Errorf("None of the identity's organizational units [%v] are in MSP %s", id.GetOrganizationalUnits(), msp.name)
973990
}
974991
}

msp/mspwithintermediatecas_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ func TestMSPWithIntermediateCAs(t *testing.T) {
5353
assert.Error(t, err)
5454
}
5555

56+
func TestMSPWithExternalIntermediateCAs(t *testing.T) {
57+
// testdata/external contains the credentials for a test MSP setup
58+
// identical to testdata/intermediate with the exception that it has
59+
// been generated independently of the fabric environment using
60+
// openssl. Sanitizing certificates may cause a change in the
61+
// signature algorithm used from that used in original
62+
// certificate file. Hashes of raw certificate bytes and
63+
// byte to byte comparisons between the raw certificate and the
64+
// one imported into the MSP could falsely fail.
65+
66+
thisMSP := getLocalMSP(t, "testdata/external")
67+
68+
// This MSP will trust any cert signed only by the intermediate
69+
70+
id, err := thisMSP.GetDefaultSigningIdentity()
71+
assert.NoError(t, err)
72+
73+
// ensure that we validate correctly the identity
74+
err = thisMSP.Validate(id.GetPublicVersion())
75+
assert.NoError(t, err)
76+
}
77+
5678
func TestIntermediateCAIdentityValidity(t *testing.T) {
5779
// testdata/intermediate contains the credentials for a test MSP setup that has
5880
// 1) a key and a signcert (used to populate the default signing identity);

msp/ouconfig_test.go

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

1919
import (
20+
"path/filepath"
2021
"testing"
2122

23+
"github.com/hyperledger/fabric/bccsp/sw"
2224
"github.com/stretchr/testify/assert"
2325
)
2426

@@ -50,3 +52,30 @@ func TestBadConfigOUCert(t *testing.T) {
5052
err = thisMSP.Setup(conf)
5153
assert.Error(t, err)
5254
}
55+
56+
func TestValidateIntermediateConfigOU(t *testing.T) {
57+
// testdata/external:
58+
// the configuration is such that only identities with
59+
// OU=Hyperledger Testing and signed by the intermediate ca should be validated
60+
thisMSP := getLocalMSP(t, "testdata/external")
61+
62+
id, err := thisMSP.GetDefaultSigningIdentity()
63+
assert.NoError(t, err)
64+
65+
err = id.Validate()
66+
assert.NoError(t, err)
67+
68+
conf, err := GetLocalMspConfig("testdata/external", nil, "DEFAULT")
69+
assert.NoError(t, err)
70+
71+
thisMSP, err = NewBccspMsp()
72+
assert.NoError(t, err)
73+
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join("testdata/external", "keystore"), true)
74+
assert.NoError(t, err)
75+
csp, err := sw.New(256, "SHA2", ks)
76+
assert.NoError(t, err)
77+
thisMSP.(*bccspmsp).bccsp = csp
78+
79+
err = thisMSP.Setup(conf)
80+
assert.NoError(t, err)
81+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICnTCCAkOgAwIBAgICEAEwCgYIKoZIzj0EAwIwgbkxCzAJBgNVBAYTAkhLMSMw
3+
IQYDVQQIDBpDZW50cmFsICYgV2VzdGVybiBEaXN0cmljdDETMBEGA1UEBwwKU2hl
4+
dW5nIFdhbjEWMBQGA1UECgwNUGFzc0tpdCwgSW5jLjEcMBoGA1UECwwTSHlwZXJs
5+
ZWRnZXIgVGVzdGluZzE6MDgGA1UEAwwxUGFzc0tpdCwgaW5jLiBIeXBlcmxlZGdl
6+
ciBUZXN0aW5nIEludGVybWVkaWF0ZSBDQTAeFw0xNzA1MjEwNTMzMjJaFw0yNzA1
7+
MjEwNTMzMjJaMIGVMQswCQYDVQQGEwJISzEjMCEGA1UECAwaQ2VudHJhbCAmIFdl
8+
c3Rlcm4gRGlzdHJpY3QxEzARBgNVBAcMClNoZXVuZyBXYW4xFjAUBgNVBAoMDVBh
9+
c3NLaXQsIEluYy4xHDAaBgNVBAsME0h5cGVybGVkZ2VyIFRlc3RpbmcxFjAUBgNV
10+
BAMMDVRlc3RpbmcvQWRtaW4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjcy4w
11+
cnFYy/gk+LhRdpDhZTVglV2K7M4neXqYn/4pdBgKgtXYXGVW4S5/hBrq3n6CfTAY
12+
R+VBzxk6kSzSCrWQo10wWzAJBgNVHRMEAjAAMB0GA1UdDgQWBBSyNnQXoP7Fw/ZZ
13+
vPYYSwrgcnGeKjAfBgNVHSMEGDAWgBRl2UHkjIPnmo8DNz0gHfpNPHJlYTAOBgNV
14+
HQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwIDSAAwRQIgHJi//h3WQU2Oi4VRjIcCroEJ
15+
Kigijt/cUwWxojf+47wCIQDyUHbdA8qxW9m2CuHZbI3eKEHhqUlhemU0VbG0BIor
16+
Pg==
17+
-----END CERTIFICATE-----
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICNTCCAdqgAwIBAgIJAKveeQnW2k9/MAoGCCqGSM49BAMCMG0xCzAJBgNVBAYT
3+
AlVTMREwDwYDVQQIDAhEZWxhd2FyZTETMBEGA1UEBwwKV2lsbWluZ3RvbjEWMBQG
4+
A1UECgwNUGFzc0tpdCwgSW5jLjEeMBwGA1UEAwwVUGFzc0tpdCwgSW5jLiBSb290
5+
IENBMB4XDTE3MDUyMTA1MDc1N1oXDTQ3MDUyMTA1MDc1N1owbTELMAkGA1UEBhMC
6+
VVMxETAPBgNVBAgMCERlbGF3YXJlMRMwEQYDVQQHDApXaWxtaW5ndG9uMRYwFAYD
7+
VQQKDA1QYXNzS2l0LCBJbmMuMR4wHAYDVQQDDBVQYXNzS2l0LCBJbmMuIFJvb3Qg
8+
Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ4gQhu+Br8vv2X+i9bsxcdP5kk
9+
wG5hOyJZ5o7QwfGibeuIdno4ZvbvRoZTlJwiPXdbUhHDIpZL99UClMj2CYpPo2Mw
10+
YTAdBgNVHQ4EFgQUH7y0pJdCaLecYlWuwticHsa+50wwHwYDVR0jBBgwFoAUH7y0
11+
pJdCaLecYlWuwticHsa+50wwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
12+
AQYwCgYIKoZIzj0EAwIDSQAwRgIhAP2Nbs/X36XQu6saYPaUkflvFIPBi07JKnY2
13+
MN+LWGyKAiEAwbTlddmOZOIwI3KlAN/n5szqEFE/ksYBHPJ6+9yb4NY=
14+
-----END CERTIFICATE-----

msp/testdata/external/config.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
OrganizationalUnitIdentifiers:
2+
- Certificate: "intermediatecerts/intermediatecert.pem"
3+
OrganizationalUnitIdentifier: "Hyperledger Testing"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICtDCCAlugAwIBAgICEAEwCgYIKoZIzj0EAwIwbTELMAkGA1UEBhMCVVMxETAP
3+
BgNVBAgMCERlbGF3YXJlMRMwEQYDVQQHDApXaWxtaW5ndG9uMRYwFAYDVQQKDA1Q
4+
YXNzS2l0LCBJbmMuMR4wHAYDVQQDDBVQYXNzS2l0LCBJbmMuIFJvb3QgQ0EwHhcN
5+
MTcwNTIxMDUxMzE4WhcNMzcwNTIxMDUxMzE4WjCBuTELMAkGA1UEBhMCSEsxIzAh
6+
BgNVBAgMGkNlbnRyYWwgJiBXZXN0ZXJuIERpc3RyaWN0MRMwEQYDVQQHDApTaGV1
7+
bmcgV2FuMRYwFAYDVQQKDA1QYXNzS2l0LCBJbmMuMRwwGgYDVQQLDBNIeXBlcmxl
8+
ZGdlciBUZXN0aW5nMTowOAYDVQQDDDFQYXNzS2l0LCBpbmMuIEh5cGVybGVkZ2Vy
9+
IFRlc3RpbmcgSW50ZXJtZWRpYXRlIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
10+
QgAERrxO6dbpvlNO+Il/dMvaJZ0lpuh94jv48DlP+hi1ekVBJa/R6/jY8HVOalG3
11+
EXwyeOv0DOl9KrMJcQqAIyvyKqOBnTCBmjAdBgNVHQ4EFgQUZdlB5IyD55qPAzc9
12+
IB36TTxyZWEwHwYDVR0jBBgwFoAUH7y0pJdCaLecYlWuwticHsa+50wwDwYDVR0T
13+
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwNwYDVR0fBDAwLjAsoCqgKIYmaHR0
14+
cHM6Ly9zc2wucGFzc2tpdC5jb20vZWNyb290LmNybC5wZW0wCgYIKoZIzj0EAwID
15+
RwAwRAIgYJ7aV3f/e9pMT5Ozc0aK2WWM5Gl/PDQ2VuM+0faVdlICIGOvS8k09R4g
16+
vDx8XOyDf2igFFUQ06abcufUcf6oj70d
17+
-----END CERTIFICATE-----
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIHACRdTlDyxk3FDWv2ysl6OnwX5KEKS7TiCOUOGkgq70oAoGCCqGSM49
3+
AwEHoUQDQgAEo3MuMHJxWMv4JPi4UXaQ4WU1YJVdiuzOJ3l6mJ/+KXQYCoLV2Fxl
4+
VuEuf4Qa6t5+gn0wGEflQc8ZOpEs0gq1kA==
5+
-----END EC PRIVATE KEY-----
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICnTCCAkOgAwIBAgICEAEwCgYIKoZIzj0EAwIwgbkxCzAJBgNVBAYTAkhLMSMw
3+
IQYDVQQIDBpDZW50cmFsICYgV2VzdGVybiBEaXN0cmljdDETMBEGA1UEBwwKU2hl
4+
dW5nIFdhbjEWMBQGA1UECgwNUGFzc0tpdCwgSW5jLjEcMBoGA1UECwwTSHlwZXJs
5+
ZWRnZXIgVGVzdGluZzE6MDgGA1UEAwwxUGFzc0tpdCwgaW5jLiBIeXBlcmxlZGdl
6+
ciBUZXN0aW5nIEludGVybWVkaWF0ZSBDQTAeFw0xNzA1MjEwNTMzMjJaFw0yNzA1
7+
MjEwNTMzMjJaMIGVMQswCQYDVQQGEwJISzEjMCEGA1UECAwaQ2VudHJhbCAmIFdl
8+
c3Rlcm4gRGlzdHJpY3QxEzARBgNVBAcMClNoZXVuZyBXYW4xFjAUBgNVBAoMDVBh
9+
c3NLaXQsIEluYy4xHDAaBgNVBAsME0h5cGVybGVkZ2VyIFRlc3RpbmcxFjAUBgNV
10+
BAMMDVRlc3RpbmcvQWRtaW4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjcy4w
11+
cnFYy/gk+LhRdpDhZTVglV2K7M4neXqYn/4pdBgKgtXYXGVW4S5/hBrq3n6CfTAY
12+
R+VBzxk6kSzSCrWQo10wWzAJBgNVHRMEAjAAMB0GA1UdDgQWBBSyNnQXoP7Fw/ZZ
13+
vPYYSwrgcnGeKjAfBgNVHSMEGDAWgBRl2UHkjIPnmo8DNz0gHfpNPHJlYTAOBgNV
14+
HQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwIDSAAwRQIgHJi//h3WQU2Oi4VRjIcCroEJ
15+
Kigijt/cUwWxojf+47wCIQDyUHbdA8qxW9m2CuHZbI3eKEHhqUlhemU0VbG0BIor
16+
Pg==
17+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)