diff --git a/msp/mspimpl.go b/msp/mspimpl.go index 9e77ac9449f..06fe7d9d44e 100644 --- a/msp/mspimpl.go +++ b/msp/mspimpl.go @@ -10,13 +10,9 @@ import ( "bytes" "crypto/x509" "crypto/x509/pkix" - "encoding/asn1" "encoding/hex" "encoding/pem" "fmt" - "math/big" - "reflect" - "time" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/bccsp" @@ -161,86 +157,6 @@ func (msp *bccspmsp) getSigningIdentityFromConf(sidInfo *m.SigningIdentityInfo) return newSigningIdentity(idPub.(*identity).cert, idPub.(*identity).pk, peerSigner, msp) } -/* - This is the definition of the ASN.1 marshalling of AuthorityKeyIdentifier - from https://www.ietf.org/rfc/rfc5280.txt - - AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - - KeyIdentifier ::= OCTET STRING - - CertificateSerialNumber ::= INTEGER - -*/ - -type authorityKeyIdentifier struct { - KeyIdentifier []byte `asn1:"optional,tag:0"` - AuthorityCertIssuer []byte `asn1:"optional,tag:1"` - AuthorityCertSerialNumber big.Int `asn1:"optional,tag:2"` -} - -// getAuthorityKeyIdentifierFromCrl returns the Authority Key Identifier -// for the supplied CRL. The authority key identifier can be used to identify -// the public key corresponding to the private key which was used to sign the CRL. -func getAuthorityKeyIdentifierFromCrl(crl *pkix.CertificateList) ([]byte, error) { - aki := authorityKeyIdentifier{} - - for _, ext := range crl.TBSCertList.Extensions { - // Authority Key Identifier is identified by the following ASN.1 tag - // authorityKeyIdentifier (2 5 29 35) (see https://tools.ietf.org/html/rfc3280.html) - if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 35}) { - _, err := asn1.Unmarshal(ext.Value, &aki) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal AKI") - } - - return aki.KeyIdentifier, nil - } - } - - return nil, errors.New("authorityKeyIdentifier not found in certificate") -} - -// getSubjectKeyIdentifierFromCert returns the Subject Key Identifier for the supplied certificate -// Subject Key Identifier is an identifier of the public key of this certificate -func getSubjectKeyIdentifierFromCert(cert *x509.Certificate) ([]byte, error) { - var SKI []byte - - for _, ext := range cert.Extensions { - // Subject Key Identifier is identified by the following ASN.1 tag - // subjectKeyIdentifier (2 5 29 14) (see https://tools.ietf.org/html/rfc3280.html) - if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 14}) { - _, err := asn1.Unmarshal(ext.Value, &SKI) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal Subject Key Identifier") - } - - return SKI, nil - } - } - - return nil, errors.New("subjectKeyIdentifier not found in certificate") -} - -// isCACert does a few checks on the certificate, -// assuming it's a CA; it returns true if all looks good -// and false otherwise -func isCACert(cert *x509.Certificate) bool { - _, err := getSubjectKeyIdentifierFromCert(cert) - if err != nil { - return false - } - - if !cert.IsCA { - return false - } - - return true -} - // Setup sets up the internal data structures // for this MSP, given an MSPConfig ref; it // returns nil in case of success or an error otherwise @@ -617,309 +533,6 @@ func (msp *bccspmsp) getCertificationChainIdentifierFromChain(chain []*x509.Cert return hf.Sum(nil), nil } -func (msp *bccspmsp) setupCrypto(conf *m.FabricMSPConfig) error { - msp.cryptoConfig = conf.CryptoConfig - if msp.cryptoConfig == nil { - // Move to defaults - msp.cryptoConfig = &m.FabricCryptoConfig{ - SignatureHashFamily: bccsp.SHA2, - IdentityIdentifierHashFunction: bccsp.SHA256, - } - mspLogger.Debugf("CryptoConfig was nil. Move to defaults.") - } - if msp.cryptoConfig.SignatureHashFamily == "" { - msp.cryptoConfig.SignatureHashFamily = bccsp.SHA2 - mspLogger.Debugf("CryptoConfig.SignatureHashFamily was nil. Move to defaults.") - } - if msp.cryptoConfig.IdentityIdentifierHashFunction == "" { - msp.cryptoConfig.IdentityIdentifierHashFunction = bccsp.SHA256 - mspLogger.Debugf("CryptoConfig.IdentityIdentifierHashFunction was nil. Move to defaults.") - } - - return nil -} - -func (msp *bccspmsp) setupCAs(conf *m.FabricMSPConfig) error { - // make and fill the set of CA certs - we expect them to be there - if len(conf.RootCerts) == 0 { - return errors.New("expected at least one CA certificate") - } - - // pre-create the verify options with roots and intermediates. - // This is needed to make certificate sanitation working. - // Recall that sanitization is applied also to root CA and intermediate - // CA certificates. After their sanitization is done, the opts - // will be recreated using the sanitized certs. - msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} - for _, v := range conf.RootCerts { - cert, err := msp.getCertFromPem(v) - if err != nil { - return err - } - msp.opts.Roots.AddCert(cert) - } - for _, v := range conf.IntermediateCerts { - cert, err := msp.getCertFromPem(v) - if err != nil { - return err - } - msp.opts.Intermediates.AddCert(cert) - } - - // Load root and intermediate CA identities - // Recall that when an identity is created, its certificate gets sanitized - msp.rootCerts = make([]Identity, len(conf.RootCerts)) - for i, trustedCert := range conf.RootCerts { - id, _, err := msp.getIdentityFromConf(trustedCert) - if err != nil { - return err - } - - msp.rootCerts[i] = id - } - - // make and fill the set of intermediate certs (if present) - msp.intermediateCerts = make([]Identity, len(conf.IntermediateCerts)) - for i, trustedCert := range conf.IntermediateCerts { - id, _, err := msp.getIdentityFromConf(trustedCert) - if err != nil { - return err - } - - msp.intermediateCerts[i] = id - } - - // root CA and intermediate CA certificates are sanitized, they can be reimported - msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} - for _, id := range msp.rootCerts { - msp.opts.Roots.AddCert(id.(*identity).cert) - } - for _, id := range msp.intermediateCerts { - msp.opts.Intermediates.AddCert(id.(*identity).cert) - } - - // make and fill the set of admin certs (if present) - msp.admins = make([]Identity, len(conf.Admins)) - for i, admCert := range conf.Admins { - id, _, err := msp.getIdentityFromConf(admCert) - if err != nil { - return err - } - - msp.admins[i] = id - } - - return nil -} - -func (msp *bccspmsp) setupAdmins(conf *m.FabricMSPConfig) error { - // make and fill the set of admin certs (if present) - msp.admins = make([]Identity, len(conf.Admins)) - for i, admCert := range conf.Admins { - id, _, err := msp.getIdentityFromConf(admCert) - if err != nil { - return err - } - - msp.admins[i] = id - } - - return nil -} - -func (msp *bccspmsp) setupCRLs(conf *m.FabricMSPConfig) error { - // setup the CRL (if present) - msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList)) - for i, crlbytes := range conf.RevocationList { - crl, err := x509.ParseCRL(crlbytes) - if err != nil { - return errors.Wrap(err, "could not parse RevocationList") - } - - // TODO: pre-verify the signature on the CRL and create a map - // of CA certs to respective CRLs so that later upon - // validation we can already look up the CRL given the - // chain of the certificate to be validated - - msp.CRL[i] = crl - } - - return nil -} - -func (msp *bccspmsp) finalizeSetupCAs(config *m.FabricMSPConfig) error { - // 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 errors.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 errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", id.(*identity).cert.SerialNumber)) - } - } - - // populate certificationTreeInternalNodesMap to mark the internal nodes of the - // certification tree - msp.certificationTreeInternalNodesMap = make(map[string]bool) - for _, id := range append([]Identity{}, msp.intermediateCerts...) { - chain, err := msp.getUniqueValidationChain(id.(*identity).cert, msp.getValidityOptsForCert(id.(*identity).cert)) - if err != nil { - return errors.WithMessage(err, fmt.Sprintf("failed getting validation chain, (SN: %s)", id.(*identity).cert.SerialNumber)) - } - - // Recall chain[0] is id.(*identity).id so it does not count as a parent - for i := 1; i < len(chain); i++ { - msp.certificationTreeInternalNodesMap[string(chain[i].Raw)] = true - } - } - - return nil -} - -func (msp *bccspmsp) setupSigningIdentity(conf *m.FabricMSPConfig) error { - if conf.SigningIdentity != nil { - sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity) - if err != nil { - return err - } - - msp.signer = sid - } - - return nil -} - -func (msp *bccspmsp) setupOUs(conf *m.FabricMSPConfig) error { - msp.ouIdentifiers = make(map[string][][]byte) - for _, ou := range conf.OrganizationalUnitIdentifiers { - - // 1. check that certificate is registered in msp.rootCerts or msp.intermediateCerts - cert, err := msp.getCertFromPem(ou.Certificate) - if err != nil { - return errors.WithMessage(err, fmt.Sprintf("failed getting certificate for [%v]", ou)) - } - - // 2. Sanitize it to ensure like for like comparison - cert, err = msp.sanitizeCert(cert) - if err != nil { - return errors.WithMessage(err, "sanitizeCert failed") - } - - found := false - root := false - // Search among root certificates - for _, v := range msp.rootCerts { - if v.(*identity).cert.Equal(cert) { - found = true - root = true - break - } - } - if !found { - // Search among root intermediate certificates - for _, v := range msp.intermediateCerts { - if v.(*identity).cert.Equal(cert) { - found = true - break - } - } - } - if !found { - // Certificate not valid, reject configuration - return errors.Errorf("failed adding OU. Certificate [%v] not in root or intermediate certs", ou.Certificate) - } - - // 3. get the certification path for it - var certifiersIdentifier []byte - var chain []*x509.Certificate - if root { - chain = []*x509.Certificate{cert} - } else { - chain, err = msp.getValidationChain(cert, true) - if err != nil { - return errors.WithMessage(err, fmt.Sprintf("failed computing validation chain for [%v]", cert)) - } - } - - // 4. compute the hash of the certification path - certifiersIdentifier, err = msp.getCertificationChainIdentifierFromChain(chain) - if err != nil { - return errors.WithMessage(err, fmt.Sprintf("failed computing Certifiers Identifier for [%v]", ou.Certificate)) - } - - // Check for duplicates - found = false - for _, id := range msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] { - if bytes.Equal(id, certifiersIdentifier) { - mspLogger.Warningf("Duplicate found in ou identifiers [%s, %v]", ou.OrganizationalUnitIdentifier, id) - found = true - break - } - } - - if !found { - // No duplicates found, add it - msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] = append( - msp.ouIdentifiers[ou.OrganizationalUnitIdentifier], - certifiersIdentifier, - ) - } - } - - return nil -} - -func (msp *bccspmsp) setupTLSCAs(conf *m.FabricMSPConfig) error { - - opts := &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} - - // Load TLS root and intermediate CA identities - msp.tlsRootCerts = make([][]byte, len(conf.TlsRootCerts)) - rootCerts := make([]*x509.Certificate, len(conf.TlsRootCerts)) - for i, trustedCert := range conf.TlsRootCerts { - cert, err := msp.getCertFromPem(trustedCert) - if err != nil { - return err - } - - rootCerts[i] = cert - msp.tlsRootCerts[i] = trustedCert - opts.Roots.AddCert(cert) - } - - // make and fill the set of intermediate certs (if present) - msp.tlsIntermediateCerts = make([][]byte, len(conf.TlsIntermediateCerts)) - intermediateCerts := make([]*x509.Certificate, len(conf.TlsIntermediateCerts)) - for i, trustedCert := range conf.TlsIntermediateCerts { - cert, err := msp.getCertFromPem(trustedCert) - if err != nil { - return err - } - - intermediateCerts[i] = cert - msp.tlsIntermediateCerts[i] = trustedCert - opts.Intermediates.AddCert(cert) - } - - // ensure that our CAs are properly formed and that they are valid - for _, cert := range append(append([]*x509.Certificate{}, rootCerts...), intermediateCerts...) { - if cert == nil { - continue - } - - if !isCACert(cert) { - return errors.Errorf("CA Certificate did not have the Subject Key Identifier extension, (SN: %s)", cert.SerialNumber) - } - - if err := msp.validateTLSCAIdentity(cert, opts); err != nil { - return errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", cert.SerialNumber)) - } - } - - return nil -} - // sanitizeCert ensures that x509 certificates signed using ECDSA // do have signatures in Low-S. If this is not the case, the certificate // is regenerated to have a Low-S signature. @@ -959,160 +572,6 @@ 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 errors.WithMessage(err, "could not obtain certification chain") - } - - err = msp.validateIdentityAgainstChain(id, validationChain) - if err != nil { - return errors.WithMessage(err, "could not validate identity against certification chain") - } - - err = msp.validateIdentityOUs(id) - if err != nil { - return errors.WithMessage(err, "could not validate identity's OUs") - } - - 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, msp.getValidityOptsForCert(id.cert)) - if err != nil { - return errors.WithMessage(err, "could not obtain certification chain") - } - if len(validationChain) == 1 { - // validationChain[0] is the root CA certificate - return nil - } - - return msp.validateIdentityAgainstChain(id, validationChain) -} - -func (msp *bccspmsp) validateTLSCAIdentity(cert *x509.Certificate, opts *x509.VerifyOptions) error { - if !cert.IsCA { - return errors.New("Only CA identities can be validated") - } - - validationChain, err := msp.getUniqueValidationChain(cert, *opts) - if err != nil { - return errors.WithMessage(err, "could not obtain certification chain") - } - if len(validationChain) == 1 { - // validationChain[0] is the root CA certificate - return nil - } - - return msp.validateCertAgainstChain(cert, validationChain) -} - -func (msp *bccspmsp) validateIdentityAgainstChain(id *identity, validationChain []*x509.Certificate) error { - return msp.validateCertAgainstChain(id.cert, validationChain) -} - -func (msp *bccspmsp) validateCertAgainstChain(cert *x509.Certificate, 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 errors.WithMessage(err, "could not obtain Subject Key Identifier for signer cert") - } - - // 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 errors.WithMessage(err, "could not obtain Authority Key Identifier for crl") - } - - // 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(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 %+v", 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 { - if len(id.GetOrganizationalUnits()) == 0 { - return errors.New("the identity certificate does not contain an Organizational Unit (OU)") - } - return errors.Errorf("none of the identity's organizational units [%v] are in MSP %s", id.GetOrganizationalUnits(), msp.name) - } - } - - return nil -} - -func (msp *bccspmsp) getValidityOptsForCert(cert *x509.Certificate) x509.VerifyOptions { - // First copy the opts to override the CurrentTime field - // in order to make the certificate passing the expiration test - // independently from the real local current time. - // This is a temporary workaround for FAB-3678 - - var tempOpts x509.VerifyOptions - tempOpts.Roots = msp.opts.Roots - tempOpts.DNSName = msp.opts.DNSName - tempOpts.Intermediates = msp.opts.Intermediates - tempOpts.KeyUsages = msp.opts.KeyUsages - tempOpts.CurrentTime = cert.NotBefore.Add(time.Second) - - return tempOpts -} - // IsWellFormed checks if the given identity can be deserialized into its provider-specific form. // In this MSP implementation, well formed means that the PEM has a Type which is either // the string 'CERTIFICATE' or the Type is missing altogether. diff --git a/msp/mspimplsetup.go b/msp/mspimplsetup.go new file mode 100644 index 00000000000..910b9ff97ec --- /dev/null +++ b/msp/mspimplsetup.go @@ -0,0 +1,321 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package msp + +import ( + "bytes" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + + "github.com/hyperledger/fabric/bccsp" + m "github.com/hyperledger/fabric/protos/msp" + "github.com/pkg/errors" +) + +func (msp *bccspmsp) setupCrypto(conf *m.FabricMSPConfig) error { + msp.cryptoConfig = conf.CryptoConfig + if msp.cryptoConfig == nil { + // Move to defaults + msp.cryptoConfig = &m.FabricCryptoConfig{ + SignatureHashFamily: bccsp.SHA2, + IdentityIdentifierHashFunction: bccsp.SHA256, + } + mspLogger.Debugf("CryptoConfig was nil. Move to defaults.") + } + if msp.cryptoConfig.SignatureHashFamily == "" { + msp.cryptoConfig.SignatureHashFamily = bccsp.SHA2 + mspLogger.Debugf("CryptoConfig.SignatureHashFamily was nil. Move to defaults.") + } + if msp.cryptoConfig.IdentityIdentifierHashFunction == "" { + msp.cryptoConfig.IdentityIdentifierHashFunction = bccsp.SHA256 + mspLogger.Debugf("CryptoConfig.IdentityIdentifierHashFunction was nil. Move to defaults.") + } + + return nil +} + +func (msp *bccspmsp) setupCAs(conf *m.FabricMSPConfig) error { + // make and fill the set of CA certs - we expect them to be there + if len(conf.RootCerts) == 0 { + return errors.New("expected at least one CA certificate") + } + + // pre-create the verify options with roots and intermediates. + // This is needed to make certificate sanitation working. + // Recall that sanitization is applied also to root CA and intermediate + // CA certificates. After their sanitization is done, the opts + // will be recreated using the sanitized certs. + msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} + for _, v := range conf.RootCerts { + cert, err := msp.getCertFromPem(v) + if err != nil { + return err + } + msp.opts.Roots.AddCert(cert) + } + for _, v := range conf.IntermediateCerts { + cert, err := msp.getCertFromPem(v) + if err != nil { + return err + } + msp.opts.Intermediates.AddCert(cert) + } + + // Load root and intermediate CA identities + // Recall that when an identity is created, its certificate gets sanitized + msp.rootCerts = make([]Identity, len(conf.RootCerts)) + for i, trustedCert := range conf.RootCerts { + id, _, err := msp.getIdentityFromConf(trustedCert) + if err != nil { + return err + } + + msp.rootCerts[i] = id + } + + // make and fill the set of intermediate certs (if present) + msp.intermediateCerts = make([]Identity, len(conf.IntermediateCerts)) + for i, trustedCert := range conf.IntermediateCerts { + id, _, err := msp.getIdentityFromConf(trustedCert) + if err != nil { + return err + } + + msp.intermediateCerts[i] = id + } + + // root CA and intermediate CA certificates are sanitized, they can be reimported + msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} + for _, id := range msp.rootCerts { + msp.opts.Roots.AddCert(id.(*identity).cert) + } + for _, id := range msp.intermediateCerts { + msp.opts.Intermediates.AddCert(id.(*identity).cert) + } + + // make and fill the set of admin certs (if present) + msp.admins = make([]Identity, len(conf.Admins)) + for i, admCert := range conf.Admins { + id, _, err := msp.getIdentityFromConf(admCert) + if err != nil { + return err + } + + msp.admins[i] = id + } + + return nil +} + +func (msp *bccspmsp) setupAdmins(conf *m.FabricMSPConfig) error { + // make and fill the set of admin certs (if present) + msp.admins = make([]Identity, len(conf.Admins)) + for i, admCert := range conf.Admins { + id, _, err := msp.getIdentityFromConf(admCert) + if err != nil { + return err + } + + msp.admins[i] = id + } + + return nil +} + +func (msp *bccspmsp) setupCRLs(conf *m.FabricMSPConfig) error { + // setup the CRL (if present) + msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList)) + for i, crlbytes := range conf.RevocationList { + crl, err := x509.ParseCRL(crlbytes) + if err != nil { + return errors.Wrap(err, "could not parse RevocationList") + } + + // TODO: pre-verify the signature on the CRL and create a map + // of CA certs to respective CRLs so that later upon + // validation we can already look up the CRL given the + // chain of the certificate to be validated + + msp.CRL[i] = crl + } + + return nil +} + +func (msp *bccspmsp) finalizeSetupCAs(config *m.FabricMSPConfig) error { + // 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 errors.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 errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", id.(*identity).cert.SerialNumber)) + } + } + + // populate certificationTreeInternalNodesMap to mark the internal nodes of the + // certification tree + msp.certificationTreeInternalNodesMap = make(map[string]bool) + for _, id := range append([]Identity{}, msp.intermediateCerts...) { + chain, err := msp.getUniqueValidationChain(id.(*identity).cert, msp.getValidityOptsForCert(id.(*identity).cert)) + if err != nil { + return errors.WithMessage(err, fmt.Sprintf("failed getting validation chain, (SN: %s)", id.(*identity).cert.SerialNumber)) + } + + // Recall chain[0] is id.(*identity).id so it does not count as a parent + for i := 1; i < len(chain); i++ { + msp.certificationTreeInternalNodesMap[string(chain[i].Raw)] = true + } + } + + return nil +} + +func (msp *bccspmsp) setupSigningIdentity(conf *m.FabricMSPConfig) error { + if conf.SigningIdentity != nil { + sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity) + if err != nil { + return err + } + + msp.signer = sid + } + + return nil +} + +func (msp *bccspmsp) setupOUs(conf *m.FabricMSPConfig) error { + msp.ouIdentifiers = make(map[string][][]byte) + for _, ou := range conf.OrganizationalUnitIdentifiers { + + // 1. check that certificate is registered in msp.rootCerts or msp.intermediateCerts + cert, err := msp.getCertFromPem(ou.Certificate) + if err != nil { + return errors.WithMessage(err, fmt.Sprintf("failed getting certificate for [%v]", ou)) + } + + // 2. Sanitize it to ensure like for like comparison + cert, err = msp.sanitizeCert(cert) + if err != nil { + return errors.WithMessage(err, "sanitizeCert failed") + } + + found := false + root := false + // Search among root certificates + for _, v := range msp.rootCerts { + if v.(*identity).cert.Equal(cert) { + found = true + root = true + break + } + } + if !found { + // Search among root intermediate certificates + for _, v := range msp.intermediateCerts { + if v.(*identity).cert.Equal(cert) { + found = true + break + } + } + } + if !found { + // Certificate not valid, reject configuration + return errors.Errorf("failed adding OU. Certificate [%v] not in root or intermediate certs", ou.Certificate) + } + + // 3. get the certification path for it + var certifiersIdentifier []byte + var chain []*x509.Certificate + if root { + chain = []*x509.Certificate{cert} + } else { + chain, err = msp.getValidationChain(cert, true) + if err != nil { + return errors.WithMessage(err, fmt.Sprintf("failed computing validation chain for [%v]", cert)) + } + } + + // 4. compute the hash of the certification path + certifiersIdentifier, err = msp.getCertificationChainIdentifierFromChain(chain) + if err != nil { + return errors.WithMessage(err, fmt.Sprintf("failed computing Certifiers Identifier for [%v]", ou.Certificate)) + } + + // Check for duplicates + found = false + for _, id := range msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] { + if bytes.Equal(id, certifiersIdentifier) { + mspLogger.Warningf("Duplicate found in ou identifiers [%s, %v]", ou.OrganizationalUnitIdentifier, id) + found = true + break + } + } + + if !found { + // No duplicates found, add it + msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] = append( + msp.ouIdentifiers[ou.OrganizationalUnitIdentifier], + certifiersIdentifier, + ) + } + } + + return nil +} + +func (msp *bccspmsp) setupTLSCAs(conf *m.FabricMSPConfig) error { + + opts := &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()} + + // Load TLS root and intermediate CA identities + msp.tlsRootCerts = make([][]byte, len(conf.TlsRootCerts)) + rootCerts := make([]*x509.Certificate, len(conf.TlsRootCerts)) + for i, trustedCert := range conf.TlsRootCerts { + cert, err := msp.getCertFromPem(trustedCert) + if err != nil { + return err + } + + rootCerts[i] = cert + msp.tlsRootCerts[i] = trustedCert + opts.Roots.AddCert(cert) + } + + // make and fill the set of intermediate certs (if present) + msp.tlsIntermediateCerts = make([][]byte, len(conf.TlsIntermediateCerts)) + intermediateCerts := make([]*x509.Certificate, len(conf.TlsIntermediateCerts)) + for i, trustedCert := range conf.TlsIntermediateCerts { + cert, err := msp.getCertFromPem(trustedCert) + if err != nil { + return err + } + + intermediateCerts[i] = cert + msp.tlsIntermediateCerts[i] = trustedCert + opts.Intermediates.AddCert(cert) + } + + // ensure that our CAs are properly formed and that they are valid + for _, cert := range append(append([]*x509.Certificate{}, rootCerts...), intermediateCerts...) { + if cert == nil { + continue + } + + if !isCACert(cert) { + return errors.Errorf("CA Certificate did not have the Subject Key Identifier extension, (SN: %s)", cert.SerialNumber) + } + + if err := msp.validateTLSCAIdentity(cert, opts); err != nil { + return errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", cert.SerialNumber)) + } + } + + return nil +} diff --git a/msp/mspimplvalidate.go b/msp/mspimplvalidate.go new file mode 100644 index 00000000000..b59fd7eba46 --- /dev/null +++ b/msp/mspimplvalidate.go @@ -0,0 +1,253 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package msp + +import ( + "bytes" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "reflect" + "time" + + "github.com/pkg/errors" +) + +func (msp *bccspmsp) validateIdentity(id *identity) error { + validationChain, err := msp.getCertificationChainForBCCSPIdentity(id) + if err != nil { + return errors.WithMessage(err, "could not obtain certification chain") + } + + err = msp.validateIdentityAgainstChain(id, validationChain) + if err != nil { + return errors.WithMessage(err, "could not validate identity against certification chain") + } + + err = msp.validateIdentityOUs(id) + if err != nil { + return errors.WithMessage(err, "could not validate identity's OUs") + } + + 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, msp.getValidityOptsForCert(id.cert)) + if err != nil { + return errors.WithMessage(err, "could not obtain certification chain") + } + if len(validationChain) == 1 { + // validationChain[0] is the root CA certificate + return nil + } + + return msp.validateIdentityAgainstChain(id, validationChain) +} + +func (msp *bccspmsp) validateTLSCAIdentity(cert *x509.Certificate, opts *x509.VerifyOptions) error { + if !cert.IsCA { + return errors.New("Only CA identities can be validated") + } + + validationChain, err := msp.getUniqueValidationChain(cert, *opts) + if err != nil { + return errors.WithMessage(err, "could not obtain certification chain") + } + if len(validationChain) == 1 { + // validationChain[0] is the root CA certificate + return nil + } + + return msp.validateCertAgainstChain(cert, validationChain) +} + +func (msp *bccspmsp) validateIdentityAgainstChain(id *identity, validationChain []*x509.Certificate) error { + return msp.validateCertAgainstChain(id.cert, validationChain) +} + +func (msp *bccspmsp) validateCertAgainstChain(cert *x509.Certificate, 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 errors.WithMessage(err, "could not obtain Subject Key Identifier for signer cert") + } + + // 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 errors.WithMessage(err, "could not obtain Authority Key Identifier for crl") + } + + // 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(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 %+v", 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 { + if len(id.GetOrganizationalUnits()) == 0 { + return errors.New("the identity certificate does not contain an Organizational Unit (OU)") + } + return errors.Errorf("none of the identity's organizational units [%v] are in MSP %s", id.GetOrganizationalUnits(), msp.name) + } + } + + return nil +} + +func (msp *bccspmsp) getValidityOptsForCert(cert *x509.Certificate) x509.VerifyOptions { + // First copy the opts to override the CurrentTime field + // in order to make the certificate passing the expiration test + // independently from the real local current time. + // This is a temporary workaround for FAB-3678 + + var tempOpts x509.VerifyOptions + tempOpts.Roots = msp.opts.Roots + tempOpts.DNSName = msp.opts.DNSName + tempOpts.Intermediates = msp.opts.Intermediates + tempOpts.KeyUsages = msp.opts.KeyUsages + tempOpts.CurrentTime = cert.NotBefore.Add(time.Second) + + return tempOpts +} + +/* + This is the definition of the ASN.1 marshalling of AuthorityKeyIdentifier + from https://www.ietf.org/rfc/rfc5280.txt + + AuthorityKeyIdentifier ::= SEQUENCE { + keyIdentifier [0] KeyIdentifier OPTIONAL, + authorityCertIssuer [1] GeneralNames OPTIONAL, + authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + + KeyIdentifier ::= OCTET STRING + + CertificateSerialNumber ::= INTEGER + +*/ + +type authorityKeyIdentifier struct { + KeyIdentifier []byte `asn1:"optional,tag:0"` + AuthorityCertIssuer []byte `asn1:"optional,tag:1"` + AuthorityCertSerialNumber big.Int `asn1:"optional,tag:2"` +} + +// getAuthorityKeyIdentifierFromCrl returns the Authority Key Identifier +// for the supplied CRL. The authority key identifier can be used to identify +// the public key corresponding to the private key which was used to sign the CRL. +func getAuthorityKeyIdentifierFromCrl(crl *pkix.CertificateList) ([]byte, error) { + aki := authorityKeyIdentifier{} + + for _, ext := range crl.TBSCertList.Extensions { + // Authority Key Identifier is identified by the following ASN.1 tag + // authorityKeyIdentifier (2 5 29 35) (see https://tools.ietf.org/html/rfc3280.html) + if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 35}) { + _, err := asn1.Unmarshal(ext.Value, &aki) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal AKI") + } + + return aki.KeyIdentifier, nil + } + } + + return nil, errors.New("authorityKeyIdentifier not found in certificate") +} + +// getSubjectKeyIdentifierFromCert returns the Subject Key Identifier for the supplied certificate +// Subject Key Identifier is an identifier of the public key of this certificate +func getSubjectKeyIdentifierFromCert(cert *x509.Certificate) ([]byte, error) { + var SKI []byte + + for _, ext := range cert.Extensions { + // Subject Key Identifier is identified by the following ASN.1 tag + // subjectKeyIdentifier (2 5 29 14) (see https://tools.ietf.org/html/rfc3280.html) + if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 14}) { + _, err := asn1.Unmarshal(ext.Value, &SKI) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal Subject Key Identifier") + } + + return SKI, nil + } + } + + return nil, errors.New("subjectKeyIdentifier not found in certificate") +} + +// isCACert does a few checks on the certificate, +// assuming it's a CA; it returns true if all looks good +// and false otherwise +func isCACert(cert *x509.Certificate) bool { + _, err := getSubjectKeyIdentifierFromCert(cert) + if err != nil { + return false + } + + if !cert.IsCA { + return false + } + + return true +}