分析MSP其实应该先对权限控制源码进行分析最好,但是权限控制的主要源码在CA相关代码中比较多,所以先把这块放到后面再分析。
MSP(membership service provider),成员管理服务提供者,它是从1.0引进的一个组件,目的是抽象各个成员间的管理结构关系。它包含:证书的管理、用户认证、加密和协议等。
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的多链形态的保障。所以说,一个系统中,每个模块都不是孤立的,是有机的和其它模块或者系统融合在一起的。在分析代码时,要注意这一点,要放到更广泛的系统中去看代码,这样才会有更大的收获。