fabric源码分析之六MSP源码分析

一、介绍

分析MSP其实应该先对权限控制源码进行分析最好,但是权限控制的主要源码在CA相关代码中比较多,所以先把这块放到后面再分析。
MSP(membership service provider),成员管理服务提供者,它是从1.0引进的一个组件,目的是抽象各个成员间的管理结构关系。它包含:证书的管理、用户认证、加密和协议等。

二、MSP内容

MSP主要的内容有什么呢?其实主要就是身份认证。有没有权利在这个“组织”内工作,此处的组织不是Fabric内的组织。
MSP是基于x.509证书,利用PKI体系为每个成员分发数字证书。并通过MSP进行身份认证和权限控制。说得挺高大上,其实就是通过根证书,签发中间CA证书,再利用中间CA签发客户端证书。它主要检查三个方面:
证书的有效性,是否过期或吊销
证书的路径的检查,客户端证书-根证书
MSP的标识认证,就是身份检查,因为MSP中证书和身份是绑定的。
针对实际的MSP管理,有三种方式:
组织和MSP建立1:1关系:推荐这种方式
组织和MSP建立1:N关系:也就说大公司有N个部门进行不同的管理方式。
组织和MSP建立N:1关系:跨组织同步方便,多个公司使用同一套MSP。
在进行MSP设计管理时,要注意将不同的CA证书存储到不同的路径,区分管理员和不同的根证书(如CA根证书和TLS根证书),严格管理证书的吊销和更新。

三、源码

源码主要位于msp目录下,其它一些相关的分散在不同的应用目录下,比如common/config/locamsp,proto/msp,在sampleconfig/msp实现了一个简单的例子。更具体的目录结构请看源码。
1、相关的数据结构

type mspSigner struct {
}
//反序列化ID的接口
type IdentityDeserializer interface {
	// 反序列化身份关联到相关的MSP。如果不是,将返回错误
	DeserializeIdentity(serializedIdentity []byte) (Identity, error)

	// 反序列化的检查
	IsWellFormed(identity *msp.SerializedIdentity) error
}
type MSPManager interface {

	// 见上面的结构体
	IdentityDeserializer

	//根据配置设置MSPManager 实例
	Setup(msps []MSP) error

	//获得成员服务提供者列表
	GetMSPs() (map[string]MSP, error)
}

// MSP is the minimal Membership Service Provider Interface to be implemented
// to accommodate peer functionality
type MSP interface {

	// 见上面的结构体
	IdentityDeserializer

	//根据配置信息设置MSP实例
	Setup(config *msp.MSPConfig) error

	// GetVersion returns the version of this MSP
	GetVersion() MSPVersion

	// GetType returns the provider type
	GetType() ProviderType

	// 返回提供者ID
	GetIdentifier() (string, error)

	// 根据提供ID返回签名身份ID
	GetSigningIdentity(identifier *IdentityIdentifier) (SigningIdentity, error)

	//返回默认签名身份ID
	GetDefaultSigningIdentity() (SigningIdentity, error)

	// 返回根证书
	GetTLSRootCerts() [][]byte

	// 返回TLS中间根证书
	GetTLSIntermediateCerts() [][]byte

	// 提供的ID是否有效
	Validate(id Identity) error

	// 身份匹配,逐字节或需要MSP验证
	SatisfiesPrincipal(id Identity, principal *msp.MSPPrincipal) error
}
type Identity interface {

	// 返回ID过期时间
	ExpiresAt() time.Time

	// 返回身份ID
	GetIdentifier() *IdentityIdentifier

	// 获得MSP的相关实例的ID
	GetMSPIdentifier() string

	// MSP规则验证
	Validate() error

	// 获得组织单位如证书颁发机构等
	GetOrganizationalUnits() []*OUIdentifier

	//匿名判定
	Anonymous() bool
	// 验证消息签名
	Verify(msg []byte, sig []byte) error
	// 序列化
	Serialize() ([]byte, error)

	// MSPPrincipal检查验证,有可能逐字节比对
	SatisfiesPrincipal(principal *msp.MSPPrincipal) error
}
//扩展的ID,包含签名功能
type SigningIdentity interface {
	// Extends Identity
	Identity
	// Sign the message
	Sign(msg []byte) ([]byte, error)
	// GetPublicVersion returns the public parts of this identity
	GetPublicVersion() Identity
}

// 特殊ID
type IdentityIdentifier struct {

	// MSP提供的标识ID
	Mspid string

	// ID
	Id string
}

另外还有些小的数据结构就不贴上来了。从上面可以看到,其实数据结构也是从小到大(或者说从大到小),从ID到MSP再到MSP管理者。然后再具体到相关数据的反序列化。

2、MSP的初始化代码
无论是在前面的Peer还是Orderer启动都在分析时看到了相关的MSP的代码的初始化部分,下面看一下相关代码:

//Peer
func serve(args []string) error {
	//
	mspType := mgmt.GetLocalMSP().GetType()
  ......
  identityDeserializerFactory := func(chainID string) msp.IdentityDeserializer {
    return mgmt.GetManagerForChain(chainID)
  }
......
  membershipInfoProvider := privdata.NewMembershipInfoProvider(createSelfSignedData(), identityDeserializerFactory)
......
  signingIdentity := mgmt.GetLocalSigningIdentityOrPanic()
  serializedIdentity, err := signingIdentity.Serialize()
  ......
}
//Orderer
func Main() {
......
	initializeLocalMsp(conf)
......
}

找到的相关的部分,下面看一下具体实现的代码有什么异同。

Orderer部分:

func initializeLocalMsp(conf *localconfig.TopLevel) {
	// Load local MSP
	err := mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir, conf.General.BCCSP, conf.General.LocalMSPID)
	if err != nil { // Handle errors reading the config file
		logger.Fatal("Failed to initialize local MSP:", err)
	}
}
func LoadLocalMsp(dir string, bccspConfig *factory.FactoryOpts, mspID string) error {
	if mspID == "" {
		return errors.New("the local MSP must have an ID")
	}
  //获得本地配置,就是提供的.yaml文件
	conf, err := msp.GetLocalMspConfig(dir, bccspConfig, mspID)
	if err != nil {
		return err
	}
  //这个在前面有,取得当前MSP并配置
	return GetLocalMSP().Setup(conf)
}
func GetLocalMspConfig(dir string, bccspConfig *factory.FactoryOpts, ID string) (*msp.MSPConfig, error) {
	signcertDir := filepath.Join(dir, signcerts)
	keystoreDir := filepath.Join(dir, keystore)
	bccspConfig = SetupBCCSPKeystoreConfig(bccspConfig, keystoreDir)

	err := factory.InitFactories(bccspConfig)
	if err != nil {
		return nil, errors.WithMessage(err, "could not initialize BCCSP Factories")
	}

  //从指定路径获得PEM证书
	signcert, err := getPemMaterialFromDir(signcertDir)
	if err != nil || len(signcert) == 0 {
		return nil, errors.Wrapf(err, "could not load a valid signer certificate from directory %s", signcertDir)
	}

  //正面会和BCCSP有关系了
	/* FIXME: for now we're making the following assumptions
	1) there is exactly one signing cert
	2) BCCSP's KeyStore has the private key that matches SKI of
	   signing cert
	*/

	sigid := &msp.SigningIdentityInfo{PublicSigner: signcert[0], PrivateSigner: nil}

	return getMspConfig(dir, ID, sigid)
}
func loadLocaMSP() msp.MSP {
	// determine the type of MSP (by default, we'll use bccspMSP)
	mspType := viper.GetString("peer.localMspType")
	if mspType == "" {
		mspType = msp.ProviderTypeToString(msp.FABRIC)
	}

	var mspOpts = map[string]msp.NewOpts{
		msp.ProviderTypeToString(msp.FABRIC): &msp.BCCSPNewOpts{NewBaseOpts: msp.NewBaseOpts{Version: msp.MSPv1_4_3}},
		msp.ProviderTypeToString(msp.IDEMIX): &msp.IdemixNewOpts{NewBaseOpts: msp.NewBaseOpts{Version: msp.MSPv1_1}},
	}
	newOpts, found := mspOpts[mspType]
	if !found {
		mspLogger.Panicf("msp type " + mspType + " unknown")
	}

	mspInst, err := msp.New(newOpts)
	if err != nil {
		mspLogger.Fatalf("Failed to initialize local MSP, received err %+v", err)
	}
	switch mspType {
	case msp.ProviderTypeToString(msp.FABRIC):
		mspInst, err = cache.New(mspInst)
		if err != nil {
			mspLogger.Fatalf("Failed to initialize local MSP, received err %+v", err)
		}
	case msp.ProviderTypeToString(msp.IDEMIX):
		// Do nothing
	default:
		panic("msp type " + mspType + " unknown")
	}

	mspLogger.Debugf("Created new local MSP")

	return mspInst
}
//在New函数里会调用生成MSP的相关函数,不同的版本人会有不同的创建方法,这里取了其中一种
func newBccspMsp(version MSPVersion) (MSP, error) {
	mspLogger.Debugf("Creating BCCSP-based MSP instance")

	bccsp := factory.GetDefault()
	theMsp := &bccspmsp{}
	theMsp.version = version
	theMsp.bccsp = bccsp
	switch version {
	case MSPv1_0:
		theMsp.internalSetupFunc = theMsp.setupV1
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV1
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_1:
		theMsp.internalSetupFunc = theMsp.setupV11
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_3:
		theMsp.internalSetupFunc = theMsp.setupV11
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV13
		theMsp.internalSetupAdmin = theMsp.setupAdminsPreV143
	case MSPv1_4_3:
		theMsp.internalSetupFunc = theMsp.setupV143
		theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV143
		theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV143
		theMsp.internalSetupAdmin = theMsp.setupAdminsV143
	default:
		return nil, errors.Errorf("Invalid MSP version [%v]", version)
	}

	return theMsp, nil
}
//最后Setup
func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
	if conf1 == nil {
		return errors.New("Setup error: nil conf reference")
	}

	// given that it's an msp of type fabric, extract the MSPConfig instance
	conf := &m.FabricMSPConfig{}
	err := proto.Unmarshal(conf1.Config, conf)
	if err != nil {
		return errors.Wrap(err, "failed unmarshalling fabric msp config")
	}

	// set the name for this msp
	msp.name = conf.Name
	mspLogger.Debugf("Setting up MSP instance %s", msp.name)

	// setup
	return msp.internalSetupFunc(conf)
}

这一系列的动作,有点小复杂。
Peer部分:
GetLoclaMSP和Orderer完全一样的代码,看一下下面的反序列化注册函数:

func GetManagerForChain(chainID string) msp.MSPManager {
	m.Lock()
	defer m.Unlock()

	mspMgr, ok := mspMap[chainID]
	if !ok {
		mspLogger.Debugf("Created new msp manager for channel `%s`", chainID)
		mspMgmtMgr := &mspMgmtMgr{msp.NewMSPManager(), false}
		mspMap[chainID] = mspMgmtMgr
		mspMgr = mspMgmtMgr
	} else {
		// 类型匹配检查
		if !(reflect.TypeOf(mspMgr).Elem().Name() == "mspManagerImpl" || reflect.TypeOf(mspMgr).Elem().Name() == "mspMgmtMgr") {
			panic("Found unexpected MSPManager type.")
		}
		mspLogger.Debugf("Returning existing manager for channel '%s'", chainID)
	}
	return mspMgr
}
//直到Setup方法后才会初始化,这里只生成一个空实例
func NewMSPManager() MSPManager {
	return &mspManagerImpl{}
}
//再看membershipInfoProvider :=
//privdata.NewMembershipInfoProvider(createSelfSignedData(), identityDeserializerFactory)
func createSelfSignedData() common2.SignedData {
	sId := mgmt.GetLocalSigningIdentityOrPanic()
	msg := make([]byte, 32)
	sig, err := sId.Sign(msg)
	if err != nil {
		logger.Panicf("Failed creating self signed data because message signing failed: %v", err)
	}
	peerIdentity, err := sId.Serialize()
	if err != nil {
		logger.Panicf("Failed creating self signed data because peer identity couldn't be serialized: %v", err)
	}
	return common2.SignedData{
		Data:      msg,
		Signature: sig,
		Identity:  peerIdentity,
	}
}
//这个函数生成一个新的相关MSP实例交将上面得到相关序列化函数指针代入
func NewMembershipInfoProvider(selfSignedData common.SignedData, identityDeserializerFunc func(chainID string) msp.IdentityDeserializer) *MembershipProvider {
	return &MembershipProvider{selfSignedData: selfSignedData, IdentityDeserializerFactory: identityDeserializerFunc}
}
func GetLocalSigningIdentityOrPanic() msp.SigningIdentity {
	id, err := GetLocalMSP().GetDefaultSigningIdentity()
	if err != nil {
		mspLogger.Panicf("Failed getting local signing identity [%+v]", err)
	}
	return id
}
func (msp *bccspmsp) GetDefaultSigningIdentity() (SigningIdentity, error) {
	mspLogger.Debugf("Obtaining default signing identity")

	if msp.signer == nil {
		return nil, errors.New("this MSP does not possess a valid default signing identity")
	}

	return msp.signer, nil
}

3、交易验证
交易的验证在背书中首先会被用到,看一下代码:
背书的除了Proto部分外,主要在core/endorser这个文件夹下,重点看一下MSP相关的验证:

func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
	// start time for computing elapsed time metric for successfully endorsed proposals
	startTime := time.Now()
......

	// 0 -- check and validate
	vr, err := e.preProcess(signedProp)
  ......
}

这里可以看看这个签名的定义:

// This structure is necessary to sign the proposal which contains the header
// and the payload. Without this structure, we would have to concatenate the
// header and the payload to verify the signature, which could be expensive
// with large payload
//
// When an endorser receives a SignedProposal message, it should verify the
// signature over the proposal bytes. This verification requires the following
// steps:
// 1. Verification of the validity of the certificate that was used to produce
//    the signature.  The certificate will be available once proposalBytes has
//    been unmarshalled to a Proposal message, and Proposal.header has been
//    unmarshalled to a Header message. While this unmarshalling-before-verifying
//    might not be ideal, it is unavoidable because i) the signature needs to also
//    protect the signing certificate; ii) it is desirable that Header is created
//    once by the client and never changed (for the sake of accountability and
//    non-repudiation). Note also that it is actually impossible to conclusively
//    verify the validity of the certificate included in a Proposal, because the
//    proposal needs to first be endorsed and ordered with respect to certificate
//    expiration transactions. Still, it is useful to pre-filter expired
//    certificates at this stage.
// 2. Verification that the certificate is trusted (signed by a trusted CA) and
//    that it is allowed to transact with us (with respect to some ACLs);
// 3. Verification that the signature on proposalBytes is valid;
// 4. Detect replay attacks;
type SignedProposal struct {
	// The bytes of Proposal
	ProposalBytes []byte `protobuf:"bytes,1,opt,name=proposal_bytes,json=proposalBytes,proto3" json:"proposal_bytes,omitempty"`
	// Signaure over proposalBytes; this signature is to be verified against
	// the creator identity contained in the header of the Proposal message
	// marshaled as proposalBytes
	Signature            []byte   `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

注释写的很清楚,接着往下看:

func (e *Endorser) preProcess(signedProp *pb.SignedProposal) (*validateResult, error) {
	vr := &validateResult{}
	// at first, we check whether the message is valid
	prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)

	if err != nil {
		e.Metrics.ProposalValidationFailed.Add(1)
		vr.resp = &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}
		return vr, err
	}
  ......
}
func ValidateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
......
  //得到Proposal的比特流
  prop, err := utils.GetProposal(signedProp.ProposalBytes)
  if err != nil {
  return nil, nil, nil, err
  }
	// 1) look at the ProposalHeader
	hdr, err := utils.GetHeader(prop.Header)
	if err != nil {
		return nil, nil, nil, err
	}

	// validate the header
	chdr, shdr, err := validateCommonHeader(hdr)
	if err != nil {
		return nil, nil, nil, err
	}

	// validate the signature
	err = checkSignatureFromCreator(shdr.Creator, signedProp.Signature, signedProp.ProposalBytes, chdr.ChannelId)
......
}
//把相关的数据结构取出用来传递给下面的验证
func validateCommonHeader(hdr *common.Header) (*common.ChannelHeader, *common.SignatureHeader, error) {
	if hdr == nil {
		return nil, nil, errors.New("nil header")
	}

	chdr, err := utils.UnmarshalChannelHeader(hdr.ChannelHeader)
	if err != nil {
		return nil, nil, err
	}

	shdr, err := utils.GetSignatureHeader(hdr.SignatureHeader)
	if err != nil {
		return nil, nil, err
	}

	err = validateChannelHeader(chdr)
	if err != nil {
		return nil, nil, err
	}

	err = validateSignatureHeader(shdr)
	if err != nil {
		return nil, nil, err
	}

	return chdr, shdr, nil
}
//下面的验证就顺理成章--基本可以看到相关的基础接口的实现的函数定义
func checkSignatureFromCreator(creatorBytes []byte, sig []byte, msg []byte, ChainID string) error {
	putilsLogger.Debugf("begin")

	// check for nil argument
	if creatorBytes == nil || sig == nil || msg == nil {
		return errors.New("nil arguments")
	}

	mspObj := mspmgmt.GetIdentityDeserializer(ChainID)
	if mspObj == nil {
		return errors.Errorf("could not get msp for channel [%s]", ChainID)
	}

	// get the identity of the creator
	creator, err := mspObj.DeserializeIdentity(creatorBytes)
	if err != nil {
		return errors.WithMessage(err, "MSP error")
	}

	putilsLogger.Debugf("creator is %s", creator.GetIdentifier())

	// ensure that creator is a valid certificate
	err = creator.Validate()
	if err != nil {
		return errors.WithMessage(err, "creator certificate is not valid")
	}

	putilsLogger.Debugf("creator is valid")

	// validate the signature
	err = creator.Verify(msg, sig)
	if err != nil {
		return errors.WithMessage(err, "creator's signature over the proposal is not valid")
	}

	putilsLogger.Debugf("exits successfully")

	return nil
}

其它的地方同样还有这种验证,基本类似就不再一一分析,以后遇到逐一说明。

四、总结

MSP只是Fabric中的一个基本的模块,其实他的重要性更在于联盟链的许可性。也正是通过它来完成了Channel的多链形态的保障。所以说,一个系统中,每个模块都不是孤立的,是有机的和其它模块或者系统融合在一起的。在分析代码时,要注意这一点,要放到更广泛的系统中去看代码,这样才会有更大的收获。

发布了104 篇原创文章 · 获赞 12 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/fpcc/article/details/105157222