美文网首页
ChainMaker 兼容第三方ca

ChainMaker 兼容第三方ca

作者: 冰冰大象 | 来源:发表于2021-09-14 15:14 被阅读0次

    ChainMaker v2版本新添加了一个兼容第三方ca的功能,现有如下问题

    1. 第三方ca兼容是怎么实现的
    2. 第三方ca是如何验证的
    3. 第三方ca有没有使用限制

    带着问题看源码,在SDK中 当设置好公私钥等参数,创建客户端时有一个压缩证书的方法func (cc *ChainClient) EnableCertHash() 同时也提供证书验证
    EnableCertHash里包含一个获取链配置的RPC方法

    func (cc *ChainClient) GetChainConfig() (*config.ChainConfig, error) {
    //省略
        ...
        // 这里看出这是一个调用invoke rpc的方法同时 交易类型还是TxType_QUERY_CONTRACT
        payload := cc.createPayload("", common.TxType_QUERY_CONTRACT, syscontract.SystemContract_CHAIN_CONFIG.String(),
            syscontract.ChainConfigFunction_GET_CHAIN_CONFIG.String(), nil, defaultSeq)
    
        resp, err := cc.proposalRequest(payload, nil)
        if err != nil {
            return nil, fmt.Errorf("send %s failed, %s", payload.TxType.String(), err.Error())
        }
    //省略
    ...
    }
    

    看一下服务端的invoke方法实现

    ### module/rpcserver/api_service.go
    // invoke contract according to TxType
    func (s *ApiService) invoke(tx *commonPb.Transaction, source protocol.TxSource) *commonPb.TxResponse {
    ...
        if tx.Payload.ChainId != SYSTEM_CHAIN {
               // 这里 如果不是系统链 我们就需要验证一下交易
            errCode, errMsg = s.validate(tx)
            if errCode != commonErr.ERR_CODE_OK {
                resp.Code = commonPb.TxStatusCode_INTERNAL_ERROR
                resp.Message = errMsg
                resp.TxId = tx.Payload.TxId
                return resp
            }
        }
    ...
    }
    
    
    // validate tx
    func (s *ApiService) validate(tx *commonPb.Transaction) (errCode commonErr.ErrCode, errMsg string) {
        ...
        ///  获取区块配置
        bc, err = s.chainMakerServer.GetBlockchain(tx.Payload.ChainId)
        if err != nil {
            errCode = commonErr.ERR_CODE_GET_BLOCKCHAIN
            errMsg = s.getErrMsg(errCode, err)
            s.log.Error(errMsg)
            return
        }
          // 验证交易 GetAccessControl()访问控制权限等
        if err = utils.VerifyTxWithoutPayload(tx, tx.Payload.ChainId, bc.GetAccessControl()); err != nil {
            errCode = commonErr.ERR_CODE_TX_VERIFY_FAILED
            errMsg = fmt.Sprintf("%s, %s, txId:%s, sender:%s", errCode.String(), err.Error(), tx.Payload.TxId,
                hex.EncodeToString(tx.Sender.Signer.MemberInfo))
            s.log.Error(errMsg)
            return
        }
    
        return commonErr.ERR_CODE_OK, ""
    }
    

    下一步 就到交易验证的工具模块utils

    ### module/utils/transaction.go
    // VerifyTxWithoutPayload verify a transaction with access control provider. The payload of the transaction will not be verified.
    func VerifyTxWithoutPayload(tx *commonPb.Transaction, chainId string, ac protocol.AccessControlProvider) error {
        if tx == nil {
            return errors.New("tx is nil")
        }
       // 验证交易头部  
        if err := verifyTxHeader(tx.Payload, chainId); err != nil {
            return fmt.Errorf("verify tx header failed, %s", err)
        }
       // 验证发送者身份
        if err := verifyTxAuth(tx, ac); err != nil {
            return fmt.Errorf("verify tx authentation failed, %s", err)
        }
        return nil
    }
    
    // verify transaction sender's authentication (include signature verification, cert-chain verification, access verification)
    func verifyTxAuth(t *commonPb.Transaction, ac protocol.AccessControlProvider) error {
        ...
        endorsements := []*commonPb.EndorsementEntry{t.Sender}
        txType := t.Payload.TxType
        principal, err := ac.CreatePrincipal(txType.String(), endorsements, txBytes)
        if err != nil {
            return fmt.Errorf("fail to construct authentication principal: %s", err)
        }
        // 这里套了一层
        ok, err := ac.VerifyPrincipal(principal)
        if err != nil {
            return fmt.Errorf("authentication error, %s", err)
        }
        if !ok {
            return fmt.Errorf("authentication failed")
        }
        ...
    }
    

    转到权限控制模块accesscontrol

    ###module/accesscontrol/cert_ac.go
    
    // VerifyPrincipal verifies if the principal for the resource is met
    func (cp *certACProvider) VerifyPrincipal(principal protocol.Principal) (bool, error) {
    ...
           // 包装一层
        refinedPrincipal, err := cp.refinePrincipal(principal)
        if err != nil {
            return false, fmt.Errorf("authentication failed, [%s]", err.Error())
        }
        ...
    }
    
    // all-in-one validation for signing members: certificate chain/whitelist, signature, policies
    func (cp *certACProvider) refinePrincipal(principal protocol.Principal) (protocol.Principal, error) {
        // 获取背书
        endorsements := principal.GetEndorsement()
        // 获取消息
        msg := principal.GetMessage()
        // 完善 背书信息
        refinedEndorsement := cp.refineEndorsements(endorsements, msg)
        if len(refinedEndorsement) <= 0 {
            return nil, fmt.Errorf("refine endorsements failed, all endorsers have failed verification")
        }
    
        refinedPrincipal, err := cp.CreatePrincipal(principal.GetResourceName(), refinedEndorsement, msg)
        if err != nil {
            return nil, fmt.Errorf("create principal failed: [%s]", err.Error())
        }
    
        return refinedPrincipal, nil
    }
    

    接着看一下refineEndorsements方法,这里分为2种情况,证书已经存储在缓存中,证书没有存储在缓存中

    func (cp *certACProvider) refineEndorsements(endorsements []*common.EndorsementEntry,
        msg []byte) []*common.EndorsementEntry {
    
        refinedSigners := map[string]bool{}
    // 组装后返回新的背书数据
        var refinedEndorsement []*common.EndorsementEntry
        var memInfo string
    
        for _, endorsementEntry := range endorsements {
        // 开始创建新数据
            endorsement := &common.EndorsementEntry{
                Signer: &pbac.Member{
                    OrgId:      endorsementEntry.Signer.OrgId,
                    MemberInfo: endorsementEntry.Signer.MemberInfo,
                    MemberType: endorsementEntry.Signer.MemberType,
                },
                Signature: endorsementEntry.Signature,
            }
          // 链上证书存储 分为MemberType_CERT 全证书 和 MemberType_CERT_HASH 证书哈希值两种
            if endorsement.Signer.MemberType == pbac.MemberType_CERT {
                cp.log.Debugf("target endorser uses full certificate")
                memInfo = string(endorsement.Signer.MemberInfo)
            }
            if endorsement.Signer.MemberType == pbac.MemberType_CERT_HASH {
                cp.log.Debugf("target endorser uses compressed certificate")
                memInfoBytes, ok := cp.lookUpCertCache(endorsement.Signer.MemberInfo)
                if !ok {
                    cp.log.Infof("authentication failed, unknown signer, the provided certificate ID is not registered")
                    continue
                }
                memInfo = string(memInfoBytes)
                endorsement.Signer.MemberInfo = memInfoBytes
            }
             // 从缓存中查找证书
            signerInfo, ok := cp.acService.lookUpMemberInCache(memInfo)
            if !ok {
             // 没有在缓存中,表示SDK第一次使用
                remoteMember, certChain, ok, err := cp.verifyPrincipalSignerNotInCache(endorsement, msg, memInfo)
                ...
                signerInfo = &cachedMember{
                    member:    remoteMember,
                    certChain: certChain,
                }
                //加入缓存
                cp.acService.addMemberToCache(memInfo, signerInfo)
            } else {
    // 如果证书已经存在,则验证证书
                flat, err := cp.verifyPrincipalSignerInCache(signerInfo, endorsement, msg, memInfo)
                 ...
            }
            ...
        }
        return refinedEndorsement
    }
    
    先说简单一点的 已经加入缓存了的证书 验证
    func (cp *certACProvider) verifyPrincipalSignerInCache(signerInfo *cachedMember, endorsement *common.EndorsementEntry,
        msg []byte, memInfo string) (bool, error) {
        // check CRL and certificate frozen list
         // 先判断当前证书的内容是否存在于第三方列表中trust_members中
        isTrustMember := false
        for _, v := range cp.acService.trustMembers {
            if v.MemberInfo == memInfo {
                isTrustMember = true
                break
            }
        }
       // 如果不存在则使用ChainMaker 自己的证书验证逻辑
        if !isTrustMember {
            ...
        }
      // 如果存在则用第三方的证书公钥来验证 SDK上传上来的 由改私钥签名的数据
        if err := signerInfo.member.Verify(cp.hashType, msg, endorsement.Signature); err != nil {
            ...
            return false, err
        }
        return true, nil
    }
    
    

    最终使用公钥来验签

    ### module/accesscontrol/cert_member.go
    func (cm *certMember) Verify(hashType string, msg []byte, sig []byte) error {
        hashAlgo, err := bcx509.GetHashFromSignatureAlgorithm(cm.cert.SignatureAlgorithm)
        if err != nil {
            return fmt.Errorf("cert member verify failed: get hash from signature algorithm failed: [%s]", err.Error())
        }
      // 公钥验签 
        ok, err := cm.cert.PublicKey.VerifyWithOpts(msg, sig, &bccrypto.SignOpts{
            Hash: hashAlgo,
            UID:  bccrypto.CRYPTO_DEFAULT_UID,
        })
        if err != nil {
            return fmt.Errorf("cert member verify signature failed: [%s]", err.Error())
        }
        if !ok {
            return fmt.Errorf("cert member verify signature failed: invalid signature")
        }
        return nil
    }
    

    此时验证完毕

    未加入缓存的证书 验证
    ### module/accesscontrol/cert_ac.go
    func (cp *certACProvider) verifyPrincipalSignerNotInCache(endorsement *common.EndorsementEntry, msg []byte,
        memInfo string) (remoteMember protocol.Member, certChain []*bcx509.Certificate, ok bool, err error) {
      // 根据发送者创建新Member
        remoteMember, err = cp.acService.newMember(endorsement.Signer)
        if err != nil {
            err = fmt.Errorf("new member failed: [%s]", err.Error())
            ok = false
            return
        }
       // 验证certMember 是否符合x509协议 是否具有相同的算法
        certChain, err = cp.verifyMember(remoteMember)
        if err != nil {
            err = fmt.Errorf("verify member failed: [%s]", err.Error())
            ok = false
            return
        }
      // 验证交易签名 也就是sdk 发出的交易签名 能否使用remoteMember的公钥验签
        if err = remoteMember.Verify(cp.hashType, msg, endorsement.Signature); err != nil {
            ...
            ok = false
            return
        }
        ok = true
        return
    }
    
    ### module/accesscontrol/ac_server.go
    // 创建新Member
    func (acs *accessControlService) newMember(member *pbac.Member) (protocol.Member, error) {
    // 从Cache中获取
        memberCached, ok := acs.lookUpMemberInCache(m(member.MemberInfo))
        if ok && memberCached.member.GetOrgId() == member.OrgId {
            acs.log.Debugf("member found in local cache")
            return memberCached.member, nil
        }
    // 没有则新增
        memberFactory := MemberFactory()
        return memberFactory.NewMember(member, acs)
    }
    
    ### module/accesscontrol/member_factory.go
    func (mf *memberFactory) NewMember(pbMember *pbac.Member, acs *accessControlService) (protocol.Member, error) {
        switch pbMember.MemberType {
        case pbac.MemberType_CERT, pbac.MemberType_CERT_HASH:
             // 创建Member
            return newCertMemberFromPb(pbMember, acs)
        }
        return nil, fmt.Errorf("new member failed: the member type is not supported")
    }
    
    ### module/accesscontrol/cert_member.go
    func newCertMemberFromPb(member *pbac.Member, acs *accessControlService) (*certMember, error) {
          // 先判断是否在外部兼容trust_members
        for _, v := range acs.trustMembers {
            certBlock, _ := pem.Decode([]byte(v.MemberInfo))
            if certBlock == nil {
                return nil, fmt.Errorf("new member failed, the trsut member cert is not PEM")
            }
         // 注意采用的是证书内容来比对是否相等
            if v.MemberInfo == string(member.MemberInfo) {
                var isCompressed bool
                if member.MemberType == pbac.MemberType_CERT {
                    isCompressed = false
                }
          //生成 certMember
                return newCertMember(v.OrgId, v.Role, acs.hashType, isCompressed, []byte(v.MemberInfo))
            }
        }
     // 如果不在 则按照ChainMaker 规则生成 certMember
        if member.MemberType == pbac.MemberType_CERT {
            certBlock, rest := pem.Decode(member.MemberInfo)
            if certBlock == nil {
                return newMemberFromCertPem(member.OrgId, acs.hashType, rest, false)
            }
            return newMemberFromCertPem(member.OrgId, acs.hashType, certBlock.Bytes, false)
        }
    
        if member.MemberType == pbac.MemberType_CERT_HASH {
            return newMemberFromCertPem(member.OrgId, acs.hashType, member.MemberInfo, true)
        }
    
        return nil, fmt.Errorf("setup member failed, unsupport cert member type")
    }
    
    

    至此整个证书验签 流程都简要看了一遍

    1.第三方ca兼容是怎么实现的
    答:通过代码梳理基本了解chainmaker 证书验证流程,如果是启用了trust_members,则优先使用第三方证书

    2.第三方ca是如何验证的
    答:sdk创建客户端时 用私钥签名,服务端用第三方证书公钥 验签

    3.第三方ca有没有使用限制
    答:没有任何限制,只要trust_members 上传的证书和SDK使用的私钥配对即可

    相关文章

      网友评论

          本文标题:ChainMaker 兼容第三方ca

          本文链接:https://www.haomeiwen.com/subject/uedfgltx.html