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

上篇我们知道证书是怎么生成了,接下来看下MSP启动是怎么加载这些材料的。
Hyperledger-Fabric源码分析(MSP-Setup)
文章图片
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 }

【Hyperledger-Fabric源码分析(MSP-Setup)】一个是签名摘要的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 validatedmsp.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安装结束,可以看到其实还是很讲究的,知道了这些,之后校验的实现会看得很快了。

    推荐阅读