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

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

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安装结束,可以看到其实还是很讲究的,知道了这些,之后校验的实现会看得很快了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,737评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,103评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,710评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,909评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,794评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,557评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,939评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,572评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,852评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,871评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,692评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,490评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,939评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,114评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,409评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,971评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,176评论 2 339

推荐阅读更多精彩内容