Skip to content

Commit

Permalink
Merge "[FAB-3240] Intermediate CA certs validation"
Browse files Browse the repository at this point in the history
  • Loading branch information
binhn authored and Gerrit Code Review committed May 11, 2017
2 parents f3bb8b7 + 246cd54 commit a39904a
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 a39904a

Please sign in to comment.