Skip to content

Commit

Permalink
[FAB-3240] Intermediate CA certs validation
Browse files Browse the repository at this point in the history
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 <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed May 11, 2017
1 parent 51b9f4e commit 246cd54
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 93 deletions.
229 changes: 136 additions & 93 deletions msp/mspimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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
}
25 changes: 25 additions & 0 deletions msp/revocation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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, ")
}
14 changes: 14 additions & 0 deletions msp/testdata/revokedica/admincerts/admin.pem
Original file line number Diff line number Diff line change
@@ -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-----
14 changes: 14 additions & 0 deletions msp/testdata/revokedica/cacerts/cacert.pem
Original file line number Diff line number Diff line change
@@ -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-----
9 changes: 9 additions & 0 deletions msp/testdata/revokedica/crls/crl.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN X509 CRL-----
MIIBMzCB2gIBATAKBggqhkjOPQQDAjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
Q2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEMMAoGA1UEChMDb3Jn
MQswCQYDVQQDEwJjYRcNMTcwNTExMDc0NjA3WhcNMjcwNTA5MDc0NjA3WjAjMCEC
EEzQohO1frOTEWQE+9Ws8nsXDTE3MDUxMTA3NDYwN1qgLzAtMCsGA1UdIwQkMCKA
ILOKM0SqRHTIKzbZMATaI82czZcZ4GkiMqnsbrmdNVPZMAoGCCqGSM49BAMCA0gA
MEUCIQCz/DcyUVInAUW3D/+618a/UovNdXT7guOhjMCx8nGufAIgbtoVSX6VnMc/
7ZQ6p4XhR0XMZxxD0oIKNSuqtGsEkEo=
-----END X509 CRL-----
16 changes: 16 additions & 0 deletions msp/testdata/revokedica/intermediatecerts/intermidiatecert.pem
Original file line number Diff line number Diff line change
@@ -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-----
5 changes: 5 additions & 0 deletions msp/testdata/revokedica/keystore/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYV78DPlUOeRDAsOl
VfZMheUFtsloDxt2jMQ2pEKHG9GhRANCAASeSmDdDmjMnbl8NbeVNO6Vdvb7EgK2
uG5RUBz8vsdizGP5FcZGkA21BgLkgjj9MHHqlL1xJBMS+VItLhnDSuLe
-----END PRIVATE KEY-----
14 changes: 14 additions & 0 deletions msp/testdata/revokedica/signcerts/signcert.pem
Original file line number Diff line number Diff line change
@@ -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-----

0 comments on commit 246cd54

Please sign in to comment.