ChainMaker v2版本新添加了一個(gè)兼容第三方ca的功能几苍,現(xiàn)有如下問(wèn)題
- 第三方ca兼容是怎么實(shí)現(xiàn)的
- 第三方ca是如何驗(yàn)證的
- 第三方ca有沒(méi)有使用限制
帶著問(wèn)題看源碼颇玷,在SDK中 當(dāng)設(shè)置好公私鑰等參數(shù)污桦,創(chuàng)建客戶端時(shí)有一個(gè)壓縮證書的方法func (cc *ChainClient) EnableCertHash()
同時(shí)也提供證書驗(yàn)證
EnableCertHash里包含一個(gè)獲取鏈配置的RPC方法
func (cc *ChainClient) GetChainConfig() (*config.ChainConfig, error) {
//省略
...
// 這里看出這是一個(gè)調(diào)用invoke rpc的方法同時(shí) 交易類型還是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())
}
//省略
...
}
看一下服務(wù)端的invoke
方法實(shí)現(xiàn)
### 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 {
// 這里 如果不是系統(tǒng)鏈 我們就需要驗(yàn)證一下交易
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) {
...
/// 獲取區(qū)塊配置
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
}
// 驗(yàn)證交易 GetAccessControl()訪問(wèn)控制權(quán)限等
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, ""
}
下一步 就到交易驗(yàn)證的工具模塊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")
}
// 驗(yàn)證交易頭部
if err := verifyTxHeader(tx.Payload, chainId); err != nil {
return fmt.Errorf("verify tx header failed, %s", err)
}
// 驗(yàn)證發(fā)送者身份
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")
}
...
}
轉(zhuǎn)到權(quán)限控制模塊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種情況,證書已經(jīng)存儲(chǔ)在緩存中误算,證書沒(méi)有存儲(chǔ)在緩存中
func (cp *certACProvider) refineEndorsements(endorsements []*common.EndorsementEntry,
msg []byte) []*common.EndorsementEntry {
refinedSigners := map[string]bool{}
// 組裝后返回新的背書數(shù)據(jù)
var refinedEndorsement []*common.EndorsementEntry
var memInfo string
for _, endorsementEntry := range endorsements {
// 開(kāi)始創(chuàng)建新數(shù)據(jù)
endorsement := &common.EndorsementEntry{
Signer: &pbac.Member{
OrgId: endorsementEntry.Signer.OrgId,
MemberInfo: endorsementEntry.Signer.MemberInfo,
MemberType: endorsementEntry.Signer.MemberType,
},
Signature: endorsementEntry.Signature,
}
// 鏈上證書存儲(chǔ) 分為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 {
// 沒(méi)有在緩存中束倍,表示SDK第一次使用
remoteMember, certChain, ok, err := cp.verifyPrincipalSignerNotInCache(endorsement, msg, memInfo)
...
signerInfo = &cachedMember{
member: remoteMember,
certChain: certChain,
}
//加入緩存
cp.acService.addMemberToCache(memInfo, signerInfo)
} else {
// 如果證書已經(jīng)存在,則驗(yàn)證證書
flat, err := cp.verifyPrincipalSignerInCache(signerInfo, endorsement, msg, memInfo)
...
}
...
}
return refinedEndorsement
}
先說(shuō)簡(jiǎn)單一點(diǎn)的 已經(jīng)加入緩存了的證書 驗(yàn)證
func (cp *certACProvider) verifyPrincipalSignerInCache(signerInfo *cachedMember, endorsement *common.EndorsementEntry,
msg []byte, memInfo string) (bool, error) {
// check CRL and certificate frozen list
// 先判斷當(dāng)前證書的內(nèi)容是否存在于第三方列表中trust_members中
isTrustMember := false
for _, v := range cp.acService.trustMembers {
if v.MemberInfo == memInfo {
isTrustMember = true
break
}
}
// 如果不存在則使用ChainMaker 自己的證書驗(yàn)證邏輯
if !isTrustMember {
...
}
// 如果存在則用第三方的證書公鑰來(lái)驗(yàn)證 SDK上傳上來(lái)的 由改私鑰簽名的數(shù)據(jù)
if err := signerInfo.member.Verify(cp.hashType, msg, endorsement.Signature); err != nil {
...
return false, err
}
return true, nil
}
最終使用公鑰來(lái)驗(yàn)簽
### 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())
}
// 公鑰驗(yàn)簽
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
}
此時(shí)驗(yàn)證完畢
未加入緩存的證書 驗(yàn)證
### 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) {
// 根據(jù)發(fā)送者創(chuàng)建新Member
remoteMember, err = cp.acService.newMember(endorsement.Signer)
if err != nil {
err = fmt.Errorf("new member failed: [%s]", err.Error())
ok = false
return
}
// 驗(yàn)證certMember 是否符合x(chóng)509協(xié)議 是否具有相同的算法
certChain, err = cp.verifyMember(remoteMember)
if err != nil {
err = fmt.Errorf("verify member failed: [%s]", err.Error())
ok = false
return
}
// 驗(yàn)證交易簽名 也就是sdk 發(fā)出的交易簽名 能否使用remoteMember的公鑰驗(yàn)簽
if err = remoteMember.Verify(cp.hashType, msg, endorsement.Signature); err != nil {
...
ok = false
return
}
ok = true
return
}
### module/accesscontrol/ac_server.go
// 創(chuàng)建新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
}
// 沒(méi)有則新增
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:
// 創(chuàng)建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")
}
// 注意采用的是證書內(nèi)容來(lái)比對(duì)是否相等
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 規(guī)則生成 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")
}
至此整個(gè)證書驗(yàn)簽 流程都簡(jiǎn)要看了一遍
1.第三方ca兼容是怎么實(shí)現(xiàn)的
答:通過(guò)代碼梳理基本了解chainmaker 證書驗(yàn)證流程乱顾,如果是啟用了trust_members板祝,則優(yōu)先使用第三方證書
2.第三方ca是如何驗(yàn)證的
答:sdk創(chuàng)建客戶端時(shí) 用私鑰簽名,服務(wù)端用第三方證書公鑰 驗(yàn)簽
3.第三方ca有沒(méi)有使用限制
答:沒(méi)有任何限制走净,只要trust_members 上傳的證書和SDK使用的私鑰配對(duì)即可