Hyperledger-Fabric源码分析(MSP-Setup)

上篇我们知道证书是怎么生成了,接下来看下MSP启动是怎么加载这些材料的。

Hyperledger-Fabric源码分析(MSP-Setup)_第1张图片
20180413180640626.JPG

setupCrypto

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
}

一个是签名摘要的hash算法,一个是身份摘要的hash算法。

setupCAs

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)
   }

   return nil
}

这里主要做两件事情

  • 拿到ca和中间ca目录的证书去组装msp的opts,可以看到这个opts由两个certpool组成。其作用之一就是底层x509在做证书校验的时候,会看是否是这两个pool里的ca签发的。
  • 接下来是将证书包装成身份加入到msp的rootCerts和intermediateCerts中,这个身份是fabric基于证书的再包装,由mspid,证书,证书摘要,证书公钥,msp组成。前面的是给底层用的,而这里是供MSP服务用的。

setupAdmins

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
}
  • 跟前面类似,生成admin的身份信息加到msp的admins里面

setupCRLs

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
}
  • 这里会去crls目录加载撤销证书列表,换句话说这里是被吊销执照的证书的集合。至于是证书主动过期还是被动吊销,不重要,在证书校验的环节,会来这里检查证书的有效与否。

finalizeSetupCAs

func (msp *bccspmsp) finalizeSetupCAs() error {
   // ensure that our CAs are properly formed and that they are valid
   for _, id := range append(append([]Identity{}, msp.rootCerts...), msp.intermediateCerts...) {
      if !id.(*identity).cert.IsCA {
         return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", id.(*identity).cert.SerialNumber)
      }
      if _, err := getSubjectKeyIdentifierFromCert(id.(*identity).cert); err != nil {
         return errors.WithMessage(err, fmt.Sprintf("CA Certificate problem with Subject Key Identifier extension, (SN: %x)", 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
}

这里主要做两件事情。

  • 校验前面加载的ca和中间ca的身份信息。
    • 证书是否有效,是否是ca啦,是否设置了SKI啊,等等。
    • 接着就是校验身份了,这里不赘述了,可以去看身份校验篇。
  • 接下来组装证书树,也就是certificationTreeInternalNodesMap,简单来说就是中间ca证书为叶子向上回溯到根证书的全部链路,组成一棵树状结构。

setupSigningIdentity

func (msp *bccspmsp) setupSigningIdentity(conf *m.FabricMSPConfig) error {
   if conf.SigningIdentity != nil {
      sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity)
      if err != nil {
         return err
      }

      expirationTime := sid.ExpiresAt()
      now := time.Now()
      if expirationTime.After(now) {
         mspLogger.Debug("Signing identity expires at", expirationTime)
      } else if expirationTime.IsZero() {
         mspLogger.Debug("Signing identity has no known expiration time")
      } else {
         return errors.Errorf("signing identity expired %v ago", now.Sub(expirationTime))
      }

      msp.signer = sid
   }

   return nil
}
  • 去signcerts目录加载ca签发的证书,这里第一时间会判断是否证书已经过期
  • 设置msp的signer,顾名思义,这个证书是用来给别人验签用的。

setupTLSCAs

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 !cert.IsCA {
         return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", cert.SerialNumber)
      }
      if _, err := getSubjectKeyIdentifierFromCert(cert); err != nil {
         return errors.WithMessage(err, fmt.Sprintf("CA Certificate problem with Subject Key Identifier extension, (SN: %x)", 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
}

这里的处理方式跟ca的部分很相似。

  • 为了跟ca相互区别,会将证书转换成身份信息存入msp的tlsRootCerts和tlsIntermediateCerts
  • 验证这些tls的ca身份信息
    • 证书是否有效,是否是ca啦,是否设置了SKI啊,等等。
    • 接着就是校验身份了,这里不赘述了,可以去看身份校验篇。这里需要注意的是前面ca的opts是保存下来以备将来之需的,而这里只是临时用一下。tlsca的证书只是tls通讯协议要用到,而msp是完全基于ca的,不一样的。

setupOUs

func (msp *bccspmsp) setupOUs(conf *m.FabricMSPConfig) error {
   msp.ouIdentifiers = make(map[string][][]byte)
   for _, ou := range conf.OrganizationalUnitIdentifiers {

      certifiersIdentifier, err := msp.getCertifiersIdentifier(ou.Certificate)
      if err != nil {
         return errors.WithMessage(err, fmt.Sprintf("failed getting certificate for [%v]", ou))
      }

      // 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
}

这里涉及到Organizational Units的概念,可以理解成公司下属的部门。是Organization的再细分。

OrganizationalUnitIdentifiers:
  - Certificate: "cacerts/cacert.pem"
    OrganizationalUnitIdentifier: "COP"
  • 而这里是在收集config.yaml中配置的OrganizationalUnitIdentifiers,内部是通过设置的证书拿到证书链,然后取摘要,保存到为“COP”为key的ouIdentifiers中。
  • 有兴趣的可以去查下这里是用来干什么的,再仔细体会下。
    • 之前的证书只是校验是不是属于这个公司,只需要看他的签发单位是根ca或中间ca就够了。而部门的验证不能这样粗糙,起码要证明是指定的证书来签发的。
    • 其次所有属于这个部门的成员,必须要由配置文件指定的证书来签发。
    • 而这里的实现是通过取待认证节点的签发单位的证书链摘要,来去msp里的“COP”的ouIdentifiers里查询,看是否已经包含了,如果能找到,说明这个节点确定是这个部门的成员。
  • 想象下,如果从cacert.pem再往下签发一个中间ca证书,来签发部门成员行不行?显然不行,按照上面的实现,是不属于该部门,除非你在配置文件里面明确设置。

setupNodeOUs

func (msp *bccspmsp) setupNodeOUs(config *m.FabricMSPConfig) error {
   if config.FabricNodeOus != nil {

      msp.ouEnforcement = config.FabricNodeOus.Enable

      // ClientOU
      msp.clientOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier}
      if len(config.FabricNodeOus.ClientOuIdentifier.Certificate) != 0 {
         certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.ClientOuIdentifier.Certificate)
         if err != nil {
            return err
         }
         msp.clientOU.CertifiersIdentifier = certifiersIdentifier
      }

      // PeerOU
      msp.peerOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier}
      if len(config.FabricNodeOus.PeerOuIdentifier.Certificate) != 0 {
         certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.PeerOuIdentifier.Certificate)
         if err != nil {
            return err
         }
         msp.peerOU.CertifiersIdentifier = certifiersIdentifier
      }

   } else {
      msp.ouEnforcement = false
   }

   return nil
}
  • 这里跟上面Organizational Units的处理类似,上面称之为组织分类,而这里是身份分类。这里我们看下配置文件。

  • 也就是说成员证书设置为peer或client,且证书链摘要能被msp承认,那么就会被msp识别为你所声明的身份。

  • NodeOUs:
      Enable: true
      ClientOUIdentifier:
        Certificate: "cacerts/cacert.pem"
        OrganizationalUnitIdentifier: "client"
      PeerOUIdentifier:
        Certificate: "cacerts/cacert.pem"
        OrganizationalUnitIdentifier: "peer"
    

postSetup

func (msp *bccspmsp) postSetupV11(conf *m.FabricMSPConfig) error {
   // Check for OU enforcement
   if !msp.ouEnforcement {
      // No enforcement required. Call post setup as per V1
      return msp.postSetupV1(conf)
   }

   // Check that admins are clients
   principalBytes, err := proto.Marshal(&m.MSPRole{Role: m.MSPRole_CLIENT, MspIdentifier: msp.name})
   if err != nil {
      return errors.Wrapf(err, "failed creating MSPRole_CLIENT")
   }
   principal := &m.MSPPrincipal{
      PrincipalClassification: m.MSPPrincipal_ROLE,
      Principal:               principalBytes}
   for i, admin := range msp.admins {
      err = admin.SatisfiesPrincipal(principal)
      if err != nil {
         return errors.WithMessage(err, fmt.Sprintf("admin %d is invalid", i))
      }
   }

   return nil
}

如果设置了config.yaml的NodeOUs为false,那么只需要验证admin的证书是否是ca签发的即可

如果是false,那需要判断,admin的签发单位身份是否是client,至于为什么这么设计,难道peer就不能成为admin?我还没领会到精髓。不过我找到官方文档的一段话。

When the classification is enabled, MSP administrators need to be clients of that MSP, meaning that their x509 certificates need to carry the OU that identifies the clients.

小结

  • 至此,整个MSP安装结束,可以看到其实还是很讲究的,知道了这些,之后校验的实现会看得很快了。

你可能感兴趣的:(Hyperledger-Fabric源码分析(MSP-Setup))