From 246cd54882acaf6cd1a6fd982486305c30b97eda Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 11 May 2017 09:59:45 +0200 Subject: [PATCH] [FAB-3240] Intermediate CA certs validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change-set modifies the bccspmsp’s setup method to ensure that intermediate CA certificates are not revoked. This is done by setting up first the CRLs and then by validating CA certificates. Tests have been added to verify that the setup fails when the setup configuration contains an intermediate CA that has been revoked. Change-Id: I8d4ef9e61de09b8f2d3909a65d02a4f5ba055038 Signed-off-by: Angelo De Caro --- msp/mspimpl.go | 229 +++++++++++------- msp/revocation_test.go | 25 ++ msp/testdata/revokedica/admincerts/admin.pem | 14 ++ msp/testdata/revokedica/cacerts/cacert.pem | 14 ++ msp/testdata/revokedica/crls/crl.pem | 9 + .../intermediatecerts/intermidiatecert.pem | 16 ++ msp/testdata/revokedica/keystore/key.pem | 5 + .../revokedica/signcerts/signcert.pem | 14 ++ 8 files changed, 233 insertions(+), 93 deletions(-) create mode 100644 msp/testdata/revokedica/admincerts/admin.pem create mode 100644 msp/testdata/revokedica/cacerts/cacert.pem create mode 100644 msp/testdata/revokedica/crls/crl.pem create mode 100644 msp/testdata/revokedica/intermediatecerts/intermidiatecert.pem create mode 100644 msp/testdata/revokedica/keystore/key.pem create mode 100644 msp/testdata/revokedica/signcerts/signcert.pem diff --git a/msp/mspimpl.go b/msp/mspimpl.go index 76e7711c098..ac84c6f58a8 100644 --- a/msp/mspimpl.go +++ b/msp/mspimpl.go @@ -368,23 +368,6 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error { msp.admins[i] = id } - // ensure that our CAs are properly formed - for _, cert := range append(append([]Identity{}, msp.rootCerts...), msp.intermediateCerts...) { - if !isCACert(cert.(*identity).cert) { - return fmt.Errorf("CA Certificate did not have the Subject Key Identifier extension, (SN: %s)", cert.(*identity).cert.SerialNumber) - } - } - - // setup the signer (if present) - if conf.SigningIdentity != nil { - sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity) - if err != nil { - return err - } - - msp.signer = sid - } - // setup the CRL (if present) msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList)) for i, crlbytes := range conf.RevocationList { @@ -401,6 +384,27 @@ func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error { msp.CRL[i] = crl } + // ensure that our CAs are properly formed and that they are valid + for _, id := range append(append([]Identity{}, msp.rootCerts...), msp.intermediateCerts...) { + if !isCACert(id.(*identity).cert) { + return fmt.Errorf("CA Certificate did not have the Subject Key Identifier extension, (SN: %s)", id.(*identity).cert.SerialNumber) + } + + if err := msp.validateCAIdentity(id.(*identity)); err != nil { + return fmt.Errorf("CA Certificate is not valid, (SN: %s) [%s]", id.(*identity).cert.SerialNumber, err) + } + } + + // setup the signer (if present) + if conf.SigningIdentity != nil { + sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity) + if err != nil { + return err + } + + msp.signer = sid + } + // setup the OUs if err := msp.setupOUs(conf); err != nil { return err @@ -461,82 +465,7 @@ func (msp *bccspmsp) Validate(id Identity) error { // this is how I can validate it given the // root of trust this MSP has case *identity: - validationChain, err := msp.getCertificationChainForBCCSPIdentity(id) - if err != nil { - return fmt.Errorf("Could not obtain certification chain, err %s", err) - } - - // here we know that the identity is valid; now we have to check whether it has been revoked - - // identify the SKI of the CA that signed this cert - SKI, err := getSubjectKeyIdentifierFromCert(validationChain[1]) - if err != nil { - return fmt.Errorf("Could not obtain Subject Key Identifier for signer cert, err %s", err) - } - - // check whether one of the CRLs we have has this cert's - // SKI as its AuthorityKeyIdentifier - for _, crl := range msp.CRL { - aki, err := getAuthorityKeyIdentifierFromCrl(crl) - if err != nil { - return fmt.Errorf("Could not obtain Authority Key Identifier for crl, err %s", err) - } - - // check if the SKI of the cert that signed us matches the AKI of any of the CRLs - if bytes.Equal(aki, SKI) { - // we have a CRL, check whether the serial number is revoked - for _, rc := range crl.TBSCertList.RevokedCertificates { - if rc.SerialNumber.Cmp(id.cert.SerialNumber) == 0 { - // We have found a CRL whose AKI matches the SKI of - // the CA (root or intermediate) that signed the - // certificate that is under validation. As a - // precaution, we verify that said CA is also the - // signer of this CRL. - err = validationChain[1].CheckCRLSignature(crl) - if err != nil { - // the CA cert that signed the certificate - // that is under validation did not sign the - // candidate CRL - skip - mspLogger.Warningf("Invalid signature over the identified CRL, error %s", err) - continue - } - - // A CRL also includes a time of revocation so that - // the CA can say "this cert is to be revoked starting - // from this time"; however here we just assume that - // revocation applies instantaneously from the time - // the MSP config is committed and used so we will not - // make use of that field - return errors.New("The certificate has been revoked") - } - } - } - } - - // Check that the identity's OUs are compatible with those recognized by this MSP, - // meaning that the intersection is not empty. - if len(msp.ouIdentifiers) > 0 { - found := false - - for _, OU := range id.GetOrganizationalUnits() { - certificationIDs, exists := msp.ouIdentifiers[OU.OrganizationalUnitIdentifier] - - if exists { - for _, certificationID := range certificationIDs { - if bytes.Equal(certificationID, OU.CertifiersIdentifier) { - found = true - break - } - } - } - } - - if !found { - return fmt.Errorf("None of the identity's organizational units [%v] are in MSP %s", id.GetOrganizationalUnits(), msp.name) - } - } - - return nil + return msp.validateIdentity(id) default: return fmt.Errorf("Identity type not recognized") } @@ -899,3 +828,117 @@ func (msp *bccspmsp) sanitizeCert(cert *x509.Certificate) (*x509.Certificate, er } return cert, nil } + +func (msp *bccspmsp) validateIdentity(id *identity) error { + validationChain, err := msp.getCertificationChainForBCCSPIdentity(id) + if err != nil { + return fmt.Errorf("Could not obtain certification chain, err %s", err) + } + + err = msp.validateIdentityAgainstChain(id, validationChain) + if err != nil { + return fmt.Errorf("Could not validate identity against certification chain, err %s", err) + } + + err = msp.validateIdentityOUs(id) + if err != nil { + return fmt.Errorf("Could not validate identity's OUs, err %s", err) + } + + return nil +} + +func (msp *bccspmsp) validateCAIdentity(id *identity) error { + if !id.cert.IsCA { + return errors.New("Only CA identities can be validated") + } + + validationChain, err := msp.getUniqueValidationChain(id.cert) + if err != nil { + return fmt.Errorf("Could not obtain certification chain, err %s", err) + } + if len(validationChain) == 1 { + // validationChain[0] is the root CA certificate + return nil + } + + return msp.validateIdentityAgainstChain(id, validationChain) +} + +func (msp *bccspmsp) validateIdentityAgainstChain(id *identity, validationChain []*x509.Certificate) error { + // here we know that the identity is valid; now we have to check whether it has been revoked + + // identify the SKI of the CA that signed this cert + SKI, err := getSubjectKeyIdentifierFromCert(validationChain[1]) + if err != nil { + return fmt.Errorf("Could not obtain Subject Key Identifier for signer cert, err %s", err) + } + + // check whether one of the CRLs we have has this cert's + // SKI as its AuthorityKeyIdentifier + for _, crl := range msp.CRL { + aki, err := getAuthorityKeyIdentifierFromCrl(crl) + if err != nil { + return fmt.Errorf("Could not obtain Authority Key Identifier for crl, err %s", err) + } + + // check if the SKI of the cert that signed us matches the AKI of any of the CRLs + if bytes.Equal(aki, SKI) { + // we have a CRL, check whether the serial number is revoked + for _, rc := range crl.TBSCertList.RevokedCertificates { + if rc.SerialNumber.Cmp(id.cert.SerialNumber) == 0 { + // We have found a CRL whose AKI matches the SKI of + // the CA (root or intermediate) that signed the + // certificate that is under validation. As a + // precaution, we verify that said CA is also the + // signer of this CRL. + err = validationChain[1].CheckCRLSignature(crl) + if err != nil { + // the CA cert that signed the certificate + // that is under validation did not sign the + // candidate CRL - skip + mspLogger.Warningf("Invalid signature over the identified CRL, error %s", err) + continue + } + + // A CRL also includes a time of revocation so that + // the CA can say "this cert is to be revoked starting + // from this time"; however here we just assume that + // revocation applies instantaneously from the time + // the MSP config is committed and used so we will not + // make use of that field + return errors.New("The certificate has been revoked") + } + } + } + } + + return nil +} + +func (msp *bccspmsp) validateIdentityOUs(id *identity) error { + // Check that the identity's OUs are compatible with those recognized by this MSP, + // meaning that the intersection is not empty. + if len(msp.ouIdentifiers) > 0 { + found := false + + for _, OU := range id.GetOrganizationalUnits() { + certificationIDs, exists := msp.ouIdentifiers[OU.OrganizationalUnitIdentifier] + + if exists { + for _, certificationID := range certificationIDs { + if bytes.Equal(certificationID, OU.CertifiersIdentifier) { + found = true + break + } + } + } + } + + if !found { + return fmt.Errorf("None of the identity's organizational units [%v] are in MSP %s", id.GetOrganizationalUnits(), msp.name) + } + } + + return nil +} diff --git a/msp/revocation_test.go b/msp/revocation_test.go index 5bc26fbd955..d718e37619c 100644 --- a/msp/revocation_test.go +++ b/msp/revocation_test.go @@ -19,6 +19,9 @@ package msp import ( "testing" + "path/filepath" + + "github.com/hyperledger/fabric/bccsp/sw" "github.com/hyperledger/fabric/protos/msp" "github.com/stretchr/testify/assert" ) @@ -58,3 +61,25 @@ func TestIdentityPolicyPrincipalAgainstRevokedIdentity(t *testing.T) { err = id.SatisfiesPrincipal(principal) assert.Error(t, err) } + +func TestRevokedIntermediateCA(t *testing.T) { + // testdata/revokedica + // 1) a key and a signcert (used to populate the default signing identity); + // 2) cacert is the CA that signed the intermediate; + // 3) a revocation list that revokes the intermediate CA cert + dir := "testdata/revokedica" + conf, err := GetLocalMspConfig(dir, nil, "DEFAULT") + assert.NoError(t, err) + + thisMSP, err := NewBccspMsp() + assert.NoError(t, err) + ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true) + assert.NoError(t, err) + csp, err := sw.New(256, "SHA2", ks) + assert.NoError(t, err) + thisMSP.(*bccspmsp).bccsp = csp + + err = thisMSP.Setup(conf) + assert.Error(t, err) + assert.Contains(t, err.Error(), "CA Certificate is not valid, ") +} diff --git a/msp/testdata/revokedica/admincerts/admin.pem b/msp/testdata/revokedica/admincerts/admin.pem new file mode 100644 index 00000000000..63f40d57c76 --- /dev/null +++ b/msp/testdata/revokedica/admincerts/admin.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLzCCAdagAwIBAgIQU9G+E1HIAZHCLdZ3j8yxOjAKBggqhkjOPQQDAjBJMQsw +CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy +YW5jaXNjbzENMAsGA1UEAxMEaWNhMTAeFw0xNzA1MTEwNzQ2MDdaFw0yNzA1MDkw +NzQ2MDdaMEoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYD +VQQHEw1TYW4gRnJhbmNpc2NvMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABJ5KYN0OaMyduXw1t5U07pV29vsSAra4blFQHPy+x2LMY/kV +xkaQDbUGAuSCOP0wceqUvXEkExL5Ui0uGcNK4t6jgZ4wgZswDgYDVR0PAQH/BAQD +AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwZgYDVR0jBF8w +XYBbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERq5JuY0xQ3oypuerfulObUxH +7wWRMatxz6+EuBPj9uqeMdfEs2Tx2DOBdb6jMvAAM0OUG32kn24T3XZZ4Ap/3DAK +BggqhkjOPQQDAgNHADBEAiAyV131BUkiTGeHLiv9dZRLftognxidV4hPPNNG80hv +YgIgKkOoJMdkDtU0VDXSZBFRlKpNidPlbreK+6FOcivS7Js= +-----END CERTIFICATE----- diff --git a/msp/testdata/revokedica/cacerts/cacert.pem b/msp/testdata/revokedica/cacerts/cacert.pem new file mode 100644 index 00000000000..40bc450d122 --- /dev/null +++ b/msp/testdata/revokedica/cacerts/cacert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICEjCCAbigAwIBAgIRAKhZ5EvzGvy83SuaCfWfeC8wCgYIKoZIzj0EAwIwVTEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG +cmFuY2lzY28xDDAKBgNVBAoTA29yZzELMAkGA1UEAxMCY2EwHhcNMTcwNTExMDc0 +NjA3WhcNMjcwNTA5MDc0NjA3WjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs +aWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEMMAoGA1UEChMDb3JnMQsw +CQYDVQQDEwJjYTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDVH2BNZpXh1h9BA +JvGD+I/cRJPGHGPIifjGGZ+lQc+j5MrrZC0+n/W+ypTO6d4GSbZgAFa1IZm2N2+c +kK5Yny+jaTBnMA4GA1UdDwEB/wQEAwIBpjAZBgNVHSUEEjAQBgRVHSUABggrBgEF +BQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdDgQiBCCzijNEqkR0yCs22TAE2iPN +nM2XGeBpIjKp7G65nTVT2TAKBggqhkjOPQQDAgNIADBFAiEAzF8huxRNn8J2zchq +SW6SBybbkxstTNt+OaIhVwRjJ5cCIEKuXlo7TYMngHiChqI8D9CNKjuMqrtGYGAI +gQrXKTsw +-----END CERTIFICATE----- diff --git a/msp/testdata/revokedica/crls/crl.pem b/msp/testdata/revokedica/crls/crl.pem new file mode 100644 index 00000000000..bb355ce6227 --- /dev/null +++ b/msp/testdata/revokedica/crls/crl.pem @@ -0,0 +1,9 @@ +-----BEGIN X509 CRL----- +MIIBMzCB2gIBATAKBggqhkjOPQQDAjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMK +Q2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEMMAoGA1UEChMDb3Jn +MQswCQYDVQQDEwJjYRcNMTcwNTExMDc0NjA3WhcNMjcwNTA5MDc0NjA3WjAjMCEC +EEzQohO1frOTEWQE+9Ws8nsXDTE3MDUxMTA3NDYwN1qgLzAtMCsGA1UdIwQkMCKA +ILOKM0SqRHTIKzbZMATaI82czZcZ4GkiMqnsbrmdNVPZMAoGCCqGSM49BAMCA0gA +MEUCIQCz/DcyUVInAUW3D/+618a/UovNdXT7guOhjMCx8nGufAIgbtoVSX6VnMc/ +7ZQ6p4XhR0XMZxxD0oIKNSuqtGsEkEo= +-----END X509 CRL----- diff --git a/msp/testdata/revokedica/intermediatecerts/intermidiatecert.pem b/msp/testdata/revokedica/intermediatecerts/intermidiatecert.pem new file mode 100644 index 00000000000..92b32c725bd --- /dev/null +++ b/msp/testdata/revokedica/intermediatecerts/intermidiatecert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICbjCCAhWgAwIBAgIQTNCiE7V+s5MRZAT71azyezAKBggqhkjOPQQDAjBVMQsw +CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy +YW5jaXNjbzEMMAoGA1UEChMDb3JnMQswCQYDVQQDEwJjYTAeFw0xNzA1MTEwNzQ2 +MDdaFw0yNzA1MDkwNzQ2MDdaMEkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp +Zm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQDEwRpY2ExMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERq5JuY0xQ3oypuerfulObUxH7wWRMatx +z6+EuBPj9uqeMdfEs2Tx2DOBdb6jMvAAM0OUG32kn24T3XZZ4Ap/3KOB0jCBzzAO +BgNVHQ8BAf8EBAMCAaYwGQYDVR0lBBIwEAYEVR0lAAYIKwYBBQUHAwEwDwYDVR0T +AQH/BAUwAwEB/zBkBgNVHQ4EXQRbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +Rq5JuY0xQ3oypuerfulObUxH7wWRMatxz6+EuBPj9uqeMdfEs2Tx2DOBdb6jMvAA +M0OUG32kn24T3XZZ4Ap/3DArBgNVHSMEJDAigCCzijNEqkR0yCs22TAE2iPNnM2X +GeBpIjKp7G65nTVT2TAKBggqhkjOPQQDAgNHADBEAiA7lweLUGOiPDiicv1UA11e +BWqsyR19QoaNkRxcdFVNIgIgE+GKKxeomIceln8PJgMIdPfWrRkiK6kVCMF1E/AU +MNo= +-----END CERTIFICATE----- diff --git a/msp/testdata/revokedica/keystore/key.pem b/msp/testdata/revokedica/keystore/key.pem new file mode 100644 index 00000000000..0c4f7acecdb --- /dev/null +++ b/msp/testdata/revokedica/keystore/key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYV78DPlUOeRDAsOl +VfZMheUFtsloDxt2jMQ2pEKHG9GhRANCAASeSmDdDmjMnbl8NbeVNO6Vdvb7EgK2 +uG5RUBz8vsdizGP5FcZGkA21BgLkgjj9MHHqlL1xJBMS+VItLhnDSuLe +-----END PRIVATE KEY----- diff --git a/msp/testdata/revokedica/signcerts/signcert.pem b/msp/testdata/revokedica/signcerts/signcert.pem new file mode 100644 index 00000000000..63f40d57c76 --- /dev/null +++ b/msp/testdata/revokedica/signcerts/signcert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLzCCAdagAwIBAgIQU9G+E1HIAZHCLdZ3j8yxOjAKBggqhkjOPQQDAjBJMQsw +CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy +YW5jaXNjbzENMAsGA1UEAxMEaWNhMTAeFw0xNzA1MTEwNzQ2MDdaFw0yNzA1MDkw +NzQ2MDdaMEoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYD +VQQHEw1TYW4gRnJhbmNpc2NvMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABJ5KYN0OaMyduXw1t5U07pV29vsSAra4blFQHPy+x2LMY/kV +xkaQDbUGAuSCOP0wceqUvXEkExL5Ui0uGcNK4t6jgZ4wgZswDgYDVR0PAQH/BAQD +AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwZgYDVR0jBF8w +XYBbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERq5JuY0xQ3oypuerfulObUxH +7wWRMatxz6+EuBPj9uqeMdfEs2Tx2DOBdb6jMvAAM0OUG32kn24T3XZZ4Ap/3DAK +BggqhkjOPQQDAgNHADBEAiAyV131BUkiTGeHLiv9dZRLftognxidV4hPPNNG80hv +YgIgKkOoJMdkDtU0VDXSZBFRlKpNidPlbreK+6FOcivS7Js= +-----END CERTIFICATE-----