MSP是Membership Service Provider的缩写,个人习惯直译为成员关系服务提供者。作用类似于,在一个运行的fabric系统网络中有众多的参与者,MSP就是为了管理这些参与者,辨识验证哪些人有资格,哪些人没资格,既维护某一个参与者的权限,也维护参与者之间的关系。关于MSP更专业的概念,请参阅Fabric文档。
这里说句题外话,最近这几篇文章其实感觉听别扭的,因为有些模块是相互牵连的,放在一起说吧文章过于复杂,主题不明,分开说又感觉讲不深,形不成体系,比如MSP和BCCSP之间就是这样。之后在start.Go完结之后,会有chaincode安装之类操作性较强的文章,在这些文章中,以具体的示例,通过原始数据在系统中被各个服务之间接收、加工、传送,以此来形成比较系统认识。
MSP的核心代码在/fabric/msp中,相关代码分布在/fabric/common/config/msp、/fabric/protos/msp、/fabric/sampleconfig/msp。主要目录结构如下:
- msp
- msp.go - 定义MSP,MSPManager,Identity,SigningIdentity等主要接口
- mspimpl.go - 实现MSP接口,结构为bccspmsp
- mspmgrimpl.go - 实现MSPManager接口,结构为mspManagerImpl
- identities.go - 实现Identity,SigningIdentity接口
- configbuilder.go - 提供读取证书文件并将其组装成MSP等接口需要的数据结构,转换配置结构体(由FactoryOpts->MSPConfig)等工具函数
- mgmt - msp的管理代码目录
- mgmt.go - 主要文件,localMsp和mspMap都在这个文件,还有多个管理函数
- common
- config
- msp/config.go - 实现了MSPManager接口,结构为MSPConfigHandler,是用来配置MSP的,用于configtx工具
- config
- protos
- msp - 原始的msp配置(MSPConfig)、定义,和对应生成的.go文件
- sampleconfig
- msp - msp所需的证书文件等的一个实例
结构图:
背书检验
fabirc源码解析11所涉及到了Endorser服务使用到了MSP对接收的SignedProposal消息进行检验,在此将详解检验过程。
函数追溯过程:ProcessProposal -> validation.ValidateProposalMessage -> checkSignatureFromCreator。在checkSignatureFromCreator函数中,进行了如下验证:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
GetIdentityDeserializer在mgmt.go中定义,返回的是一个IdentityDeserializer接口对象,实际上最终返回的是bccspmsp对象或mspManagerImpl/MSPConfigHandler对象,因为IdentityDeserializer接口从定义处的注释就可以看出其作用注意在粘合MSP和MSPManager这两个接口,这两个接口都要求实现了IdentityDeserializer接口。对应的,当返回的是bccspmsp对象时,是通过GetLocalMSP函数,其实质返回的是localMsp变量;当返回的是mspManagerImpl/MSPConfigHandler对象时,是通过GetManagerForChain函数,其实质返回的是mspMap映射的ChainID的值。
这里要另外说的是MSPConfigHandler对象,在/fabric/common/config/msp/config.go中定义,该对象将MSPManager接口作为了成员,自然也应实现了IdentityDeserializer接口,但是冲突的是,搜索源码,并没有发现其实现MSPManager接口的代码,想必是让用户自己实现或在具体使用的时候再赋予具体的MSPManager实现对象。GetManagerForChain函数中,若MSPConfigHandler的成员MSPManager为空,则直接返回nil,进而checkSignatureFromCreator函数也会直接返回。在一些test代码中,如mgmt_test.go中,实例化MSPConfigHandler对象时,其MSPManager成员直接初始化为系统实现的mspManagerImpl或直接给nil。在此将此对象先搁置,等之后的代码中遇到可解明之处了再补。
因此,mspObj是bccspmsp对象或mspManagerImpl对象,在此分别用A和B表示。两种对象实现IdentityDeserializer接口的DeserializeIdentity方法略有所不同,原因在于其自身结构的差异,但殊途同归。A是一个MSP,而B实现MSPManager,自然肩负这管理MSP的任务,因此其成员中有一个MSP的映射mspsMap,算是一堆MSP。
A和B都将接收的creatorBytes参数Unmarshal成SerializedIdentity结构对象,该对象定义在fabric/protos/msp/identities.pb.go中,且其成员Mspid和IdBytes将用于A和B的后续验证。
A验证Mspid与自身存储的name是否一致后,将IdBytes传入内调函数deserializeIdentityInternal,以进行进一步的验证;B验证以Mspid为key值的映射是否存在于自身的mspsMap中后,用switch判断映射的MSP对象的类型,若是系统实现的bccspmsp,则与A一样去调用deserializeIdentityInternal,若是用户自己实现的MSP,则再去调用用户实现的DeserializeIdentity方法,我们撇下用户自己实现的MSP的这种情况。
deserializeIdentityInternal函数的操作涉及到了mspObj对象中的
bccsp bccsp.BCCSP
成员,该成员是一个bccsp服务对象,而bccsp是fabric整个系统的加密服务的提供者,将在主题文章中详述。deserializeIdentityInternal函数接收IdBytes,并根据IdBytes调用一系列bccsp所提供的函数,如GetHashOpt
、Hash
、KeyImport
,生成identity对象所需的数据,最终生成identity对象并返回。identity对象实现了Identity接口,在msp/identities.go中定义,代表一个身份对象,也提供了验证能力(函数)。这里的身份是用来表明一个会员的身份的,包含了这个会员的名称、使用的证书、加密算法、公匙和MSP对象自身等信息,验证能力指的是其提供的Validate和Verify两个函数。使用identity对象,也就是creator接收到的值,进行验证和确认。追溯Validate和Verify两个函数,都在/fabric/msp/identities.go中定义,我们会发现最终使用的是其所包含的MSP对象的Validate和MSP对象所包含的bccsp对象的Verify函数。这里和第3步一样,使用了bccsp服务,在此不再赘述。identity validation(与certificate validation是一个意思,即证书和身份的概念是重叠的,证书就代表着身份)和signature verification这两个短语在fabric文档中是专用的,分别对应两个函数Validate和Verify,也就是说,前者验证的是身份,后者确认的是签名。
了解一个服务模块,基本上问题还是集中在两点:
- 服务模块自身能做什么。
- 系统如何使用这个模块。这里的如何包含了何时使用和如何使用两个意思。
我们接下来从这个思路入手,了解MSP服务。这里先进行一个打比方的描述和证书的解释:
打个比方:
一个集团化的大型公司,可能有一个集团总部,集团下分若干个子集团,分别涉及不同商业领域(如建设集团,商业发展集团,投资集团等子集团),每个集团下在全国各地有若干个分公司,分公司下有部门,部分中有职员,职员有管理者和普通职员之分。**
关于证书(fabric里面所用的都是x509证书):
- 证书本身是承载公匙的容器,里面最主要的就是公匙,和一些认证信息。比较证书的时候,自然比较公匙。
- SKI是当前证书的标识符,所谓标识符,一般是对公匙进行hash,得到的一段字符标识。
- SKI是当前证书的标识符,AKI是签署方的SKI,也就是签署方的公匙标识符。
- 证书是一级级信任的,比如某个CA的密匙签署了你的密匙,生成了你的证书,也就是该CA认证了你的证书,也就是你的证书就是该CA证书的下一级证书。原来的那个CA证书自己的标识符也是SKI,而对于你这个证书而言,CA那个证书的标识符是你的签署方标识符,也就是AKI。即上一级的SKI变为下一级的AKI。
- x509是证书的一种类型,具体可参看/fabric/sampleconfig下的admincerts,cacerts中的文件。## Identity和SigningIdentity接口
Identity
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
在此省略Identity接口中GetXXX相关的实现,这些都是获取身份信息的,算不上什么功能性接口,而相较于获取身份信息这种比较简单的实现,我们更想知道身份中的数据是如何形成的和这些数据可以做什么。Identity是系统成员身份的代表,而其所包含的x509证书就可以在系统中代表着这个身份,由MSP“拥有”和管理的。Identity其实不真正实现什么功能,只是比较本质的起到代表一个成员的作用(就足够了),这点可以从SatisfiesPrincipal,Validate,Verify,Serialize这样的功能性接口的实现看出:这些实现都是调用管理这个身份实例的msp的接口实现的,而身份实例自身只提供身份数据。身份在系统中,可以代表一个peer结点的身份,可以代表一个组织的身份,等身份性的对象。还有一点,如果一个身份中包含所属的组织信息(同时也就是说身份可能没有组织信息),该组织信息是被包含在x509证书中的。
从SatisfiesPrincipal这个接口实现,可以引出身份的类型之分。类型之分的原始定义在/fabric/protos/common中的msp_principal.proto和对应生成msp_principal.pb.go。结构体为MSPPrincipal,有以下三种类型的身份:
- ROLE,表示一个角色,有成员MEMBER和管理者ADMIN之分。类似于一个公司中,普通职员和经理。
- ORGANIZATION_UNIT,表示组织单位。类似于一个公司中的,一个部门。
- IDENTITY,就表示一个普通的身份。是与ORGANIZATION_UNIT相对的,类似于一个公司中,一个个体。
SigningIdentity
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
签名者身份是一个拿有“专用签名笔”的身份。自然,也用来进行签名。在MSP管理的所有身份对象中,可以指定一些成员用来对消息进行签名。在Sign实现中,使用的是“专用签名笔”crypto标准库里的Signer对象,同时用了MSP的一些功能作为签名的辅助。
可以把Identity想象成一个部门或一个个体职员,当为一个部门时,其为组织类型,当为一个个体时,又有管理者和普通职员之分。
MSP和MSPManager接口
MSP
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
关于bccspmsp的结构体中的字段,可以对照fabirc源码解析9——文档翻译之MSP的MSP配置章节所讲的一个MSP需要指定哪些数据,就很容易理解了。这里起名为bccspmsp,指的是该实现使用了bccsp作为其加密技术的提供者,正如其有一个成员bccsp一样。由对Identity接口所讲的那样,MSP实质上承载着身份验证和管理的任务。可以把MSP想象成一个子公司,每个其所管理的成员要么是公司的一个个体,要么是公司的一个部门,个体要么是经理,要么是普通职员,而且这些主体都用一个通用的身份接口Identity表示。
Setup:Setup的过程会如你想象的那样,就是对参数conf1中所携带的数据进行择选,然后填充bccspmsp的各个字段,这其中用到了bccsp的加密技术,如果某些所需的数据conf1中没有,则置默认值。这也就是说,conf1中有我们需要的数据,那么这个conf1是怎么形成的呢,它的数据是从哪儿来的?如果不想搜索源代码以查看这个接口在何时何处被调用(因为既然要调用,肯定会准备好数据,依此可以寻找conf1是怎么形成的),那么可以直接看test或mock文件,这里,比较简单直接的且能说明问题的就是/fabric/msp/mspwithintermediatecas_test.go这个测试文件,里面简单查看Setup调用之前的conf数据组装,就可以知道conf是怎么来的了。
Validate:验证身份有效,主要验证(满足)三点:
- 身份中所携带的x509证书存在于bccsmsp的rootCerts或intermediateCerts之中。这需要你比较熟悉x509证书Certificate的方法Verify的用法(追溯Validate函数可以找到),原型为
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error)
,这里使用到的参数opts就是bccsmsp成员opts,这个成员相当与一个证书地图,即指示函数去哪些证书池(CertPool)中与c这个证书进行对比。opts在bccsmsp的Setup实现中被初始化为包含rootCerts和intermediateCerts两个证书池。 - 身份中所携带的x509证书不在CRL中。这里的对比不是直接用证书本身对比,因为CRL所对应的结构体是pkix.CertificateList。
- 身份中所携带的组织信息有效。
SatisfiesPrincipal:验证给定的身份id是否与所给的principal中所描述的类型相匹配。过程就是根据身份的类型所进行的一个switch-case分支判断的过程,这个判断就是分别抽取id和principal中对应的字段信息进行对比。
MSPManager
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
可以把MSPManager想象成一个子集团,其管理集团旗下的各个子公司,而且这些子公司主体都用一个MSP接口表示。
mgmt
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
mgmt就是management的缩写,也就是管理的意思。这里的本地指代的是peer结点,而mgmt也不是一个具体的对象。可以说真正经常供peer其他模块调用和使用的函数是这里提供的函数,也即mgmt才是对外提供MSP服务的窗口。MSP数据存储的主体就是localMsp和mspMap。
- localMsp,本地MSP,管理了所有本地的身份。LoadLocalMsp是给localMsp读入MSP数据的,按部就班,攒成配置选项,然后调用Setup填充localMsp。GetXXX系列中与localMsp相关的则是向外界提供这个本地的MSP。在fabric源码分析5–kvledger的初始化中开篇所遗留的关于InitCrypto的问题,在此就可以解释一下,InitCrypto在/fabric/peer/main.go中main函数中被调用,在/fabric/peer/common/common.go中定义。而InitCrypto中就调用了LoadLocalMsp,也就初始化了peer结点本地MSP。在/fabric/orderer/main.go中,也调用了LoadLocalMsp,也就初始化了orderer结点的本地MSP。
- mspMap是一个以chaincodeID为key的映射,可以把每个chaincode当作是一个不同的商业领域,而把mspMap看作是按chaincode分的每个chaincode上的子集团的集合,这些子集团在各自的商业领域管理这自己的子公司(MSP)。GetManagerForChain函数既是数据赋予者也是数据给予者,当所给定的chaincodeID不存在时,会自动创建一个MSPManager对象,当所给的chaincodeID存在时,则返回对应的MSPManager。如在/fabric/core/scc/lscc/lscc.go中的checkInstantiationPolicy函数中调用了GetManagerForChain,获取了指定chainName的chaincode的MSPManager以供使用。
可以把mgmt想象成一个集团化公司的总部,管理各个子集团(MSPManager)并统一向外界提供自身所管理的成员数据和信息,这也就是所谓的MSP服务。