上篇我们知道证书是怎么生成了,接下来看下MSP启动是怎么加载这些材料的。
setupCrypto
func (msp *bccspmsp) setupCrypto(conf *m.FabricMSPConfig) error {
msp.cryptoConfig = conf.CryptoConfig
if msp.cryptoConfig == nil {
// Move to defaults
msp.cryptoConfig = &m.FabricCryptoConfig{
SignatureHashFamily: bccsp.SHA2,
IdentityIdentifierHashFunction: bccsp.SHA256,
}
mspLogger.Debugf("CryptoConfig was nil. Move to defaults.")
}
if msp.cryptoConfig.SignatureHashFamily == "" {
msp.cryptoConfig.SignatureHashFamily = bccsp.SHA2
mspLogger.Debugf("CryptoConfig.SignatureHashFamily was nil. Move to defaults.")
}
if msp.cryptoConfig.IdentityIdentifierHashFunction == "" {
msp.cryptoConfig.IdentityIdentifierHashFunction = bccsp.SHA256
mspLogger.Debugf("CryptoConfig.IdentityIdentifierHashFunction was nil. Move to defaults.")
}
return nil
}
一个是签名摘要的hash算法,一个是身份摘要的hash算法。
setupCAs
func (msp *bccspmsp) setupCAs(conf *m.FabricMSPConfig) error {
// make and fill the set of CA certs - we expect them to be there
if len(conf.RootCerts) == 0 {
return errors.New("expected at least one CA certificate")
}
// pre-create the verify options with roots and intermediates.
// This is needed to make certificate sanitation working.
// Recall that sanitization is applied also to root CA and intermediate
// CA certificates. After their sanitization is done, the opts
// will be recreated using the sanitized certs.
msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
for _, v := range conf.RootCerts {
cert, err := msp.getCertFromPem(v)
if err != nil {
return err
}
msp.opts.Roots.AddCert(cert)
}
for _, v := range conf.IntermediateCerts {
cert, err := msp.getCertFromPem(v)
if err != nil {
return err
}
msp.opts.Intermediates.AddCert(cert)
}
// Load root and intermediate CA identities
// Recall that when an identity is created, its certificate gets sanitized
msp.rootCerts = make([]Identity, len(conf.RootCerts))
for i, trustedCert := range conf.RootCerts {
id, _, err := msp.getIdentityFromConf(trustedCert)
if err != nil {
return err
}
msp.rootCerts[i] = id
}
// make and fill the set of intermediate certs (if present)
msp.intermediateCerts = make([]Identity, len(conf.IntermediateCerts))
for i, trustedCert := range conf.IntermediateCerts {
id, _, err := msp.getIdentityFromConf(trustedCert)
if err != nil {
return err
}
msp.intermediateCerts[i] = id
}
// root CA and intermediate CA certificates are sanitized, they can be reimported
msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
for _, id := range msp.rootCerts {
msp.opts.Roots.AddCert(id.(*identity).cert)
}
for _, id := range msp.intermediateCerts {
msp.opts.Intermediates.AddCert(id.(*identity).cert)
}
return nil
}
这里主要做两件事情
- 拿到ca和中间ca目录的证书去组装msp的opts,可以看到这个opts由两个certpool组成。其作用之一就是底层x509在做证书校验的时候,会看是否是这两个pool里的ca签发的。
- 接下来是将证书包装成身份加入到msp的rootCerts和intermediateCerts中,这个身份是fabric基于证书的再包装,由mspid,证书,证书摘要,证书公钥,msp组成。前面的是给底层用的,而这里是供MSP服务用的。
setupAdmins
func (msp *bccspmsp) setupAdmins(conf *m.FabricMSPConfig) error {
// make and fill the set of admin certs (if present)
msp.admins = make([]Identity, len(conf.Admins))
for i, admCert := range conf.Admins {
id, _, err := msp.getIdentityFromConf(admCert)
if err != nil {
return err
}
msp.admins[i] = id
}
return nil
}
- 跟前面类似,生成admin的身份信息加到msp的admins里面
setupCRLs
func (msp *bccspmsp) setupCRLs(conf *m.FabricMSPConfig) error {
// setup the CRL (if present)
msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList))
for i, crlbytes := range conf.RevocationList {
crl, err := x509.ParseCRL(crlbytes)
if err != nil {
return errors.Wrap(err, "could not parse RevocationList")
}
// TODO: pre-verify the signature on the CRL and create a map
// of CA certs to respective CRLs so that later upon
// validation we can already look up the CRL given the
// chain of the certificate to be validated
msp.CRL[i] = crl
}
return nil
}
- 这里会去crls目录加载撤销证书列表,换句话说这里是被吊销执照的证书的集合。至于是证书主动过期还是被动吊销,不重要,在证书校验的环节,会来这里检查证书的有效与否。
finalizeSetupCAs
func (msp *bccspmsp) finalizeSetupCAs() error {
// ensure that our CAs are properly formed and that they are valid
for _, id := range append(append([]Identity{}, msp.rootCerts...), msp.intermediateCerts...) {
if !id.(*identity).cert.IsCA {
return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", id.(*identity).cert.SerialNumber)
}
if _, err := getSubjectKeyIdentifierFromCert(id.(*identity).cert); err != nil {
return errors.WithMessage(err, fmt.Sprintf("CA Certificate problem with Subject Key Identifier extension, (SN: %x)", id.(*identity).cert.SerialNumber))
}
if err := msp.validateCAIdentity(id.(*identity)); err != nil {
return errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", id.(*identity).cert.SerialNumber))
}
}
// populate certificationTreeInternalNodesMap to mark the internal nodes of the
// certification tree
msp.certificationTreeInternalNodesMap = make(map[string]bool)
for _, id := range append([]Identity{}, msp.intermediateCerts...) {
chain, err := msp.getUniqueValidationChain(id.(*identity).cert, msp.getValidityOptsForCert(id.(*identity).cert))
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("failed getting validation chain, (SN: %s)", id.(*identity).cert.SerialNumber))
}
// Recall chain[0] is id.(*identity).id so it does not count as a parent
for i := 1; i < len(chain); i++ {
msp.certificationTreeInternalNodesMap[string(chain[i].Raw)] = true
}
}
return nil
}
这里主要做两件事情。
- 校验前面加载的ca和中间ca的身份信息。
- 证书是否有效,是否是ca啦,是否设置了SKI啊,等等。
- 接着就是校验身份了,这里不赘述了,可以去看身份校验篇。
- 接下来组装证书树,也就是certificationTreeInternalNodesMap,简单来说就是中间ca证书为叶子向上回溯到根证书的全部链路,组成一棵树状结构。
setupSigningIdentity
func (msp *bccspmsp) setupSigningIdentity(conf *m.FabricMSPConfig) error {
if conf.SigningIdentity != nil {
sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity)
if err != nil {
return err
}
expirationTime := sid.ExpiresAt()
now := time.Now()
if expirationTime.After(now) {
mspLogger.Debug("Signing identity expires at", expirationTime)
} else if expirationTime.IsZero() {
mspLogger.Debug("Signing identity has no known expiration time")
} else {
return errors.Errorf("signing identity expired %v ago", now.Sub(expirationTime))
}
msp.signer = sid
}
return nil
}
- 去signcerts目录加载ca签发的证书,这里第一时间会判断是否证书已经过期
- 设置msp的signer,顾名思义,这个证书是用来给别人验签用的。
setupTLSCAs
func (msp *bccspmsp) setupTLSCAs(conf *m.FabricMSPConfig) error {
opts := &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
// Load TLS root and intermediate CA identities
msp.tlsRootCerts = make([][]byte, len(conf.TlsRootCerts))
rootCerts := make([]*x509.Certificate, len(conf.TlsRootCerts))
for i, trustedCert := range conf.TlsRootCerts {
cert, err := msp.getCertFromPem(trustedCert)
if err != nil {
return err
}
rootCerts[i] = cert
msp.tlsRootCerts[i] = trustedCert
opts.Roots.AddCert(cert)
}
// make and fill the set of intermediate certs (if present)
msp.tlsIntermediateCerts = make([][]byte, len(conf.TlsIntermediateCerts))
intermediateCerts := make([]*x509.Certificate, len(conf.TlsIntermediateCerts))
for i, trustedCert := range conf.TlsIntermediateCerts {
cert, err := msp.getCertFromPem(trustedCert)
if err != nil {
return err
}
intermediateCerts[i] = cert
msp.tlsIntermediateCerts[i] = trustedCert
opts.Intermediates.AddCert(cert)
}
// ensure that our CAs are properly formed and that they are valid
for _, cert := range append(append([]*x509.Certificate{}, rootCerts...), intermediateCerts...) {
if cert == nil {
continue
}
if !cert.IsCA {
return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", cert.SerialNumber)
}
if _, err := getSubjectKeyIdentifierFromCert(cert); err != nil {
return errors.WithMessage(err, fmt.Sprintf("CA Certificate problem with Subject Key Identifier extension, (SN: %x)", cert.SerialNumber))
}
if err := msp.validateTLSCAIdentity(cert, opts); err != nil {
return errors.WithMessage(err, fmt.Sprintf("CA Certificate is not valid, (SN: %s)", cert.SerialNumber))
}
}
return nil
}
这里的处理方式跟ca的部分很相似。
- 为了跟ca相互区别,会将证书转换成身份信息存入msp的tlsRootCerts和tlsIntermediateCerts
- 验证这些tls的ca身份信息
- 证书是否有效,是否是ca啦,是否设置了SKI啊,等等。
- 接着就是校验身份了,这里不赘述了,可以去看身份校验篇。这里需要注意的是前面ca的opts是保存下来以备将来之需的,而这里只是临时用一下。tlsca的证书只是tls通讯协议要用到,而msp是完全基于ca的,不一样的。
setupOUs
func (msp *bccspmsp) setupOUs(conf *m.FabricMSPConfig) error {
msp.ouIdentifiers = make(map[string][][]byte)
for _, ou := range conf.OrganizationalUnitIdentifiers {
certifiersIdentifier, err := msp.getCertifiersIdentifier(ou.Certificate)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("failed getting certificate for [%v]", ou))
}
// Check for duplicates
found := false
for _, id := range msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] {
if bytes.Equal(id, certifiersIdentifier) {
mspLogger.Warningf("Duplicate found in ou identifiers [%s, %v]", ou.OrganizationalUnitIdentifier, id)
found = true
break
}
}
if !found {
// No duplicates found, add it
msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] = append(
msp.ouIdentifiers[ou.OrganizationalUnitIdentifier],
certifiersIdentifier,
)
}
}
return nil
}
这里涉及到Organizational Units的概念,可以理解成公司下属的部门。是Organization的再细分。
OrganizationalUnitIdentifiers: - Certificate: "cacerts/cacert.pem" OrganizationalUnitIdentifier: "COP"
- 而这里是在收集config.yaml中配置的OrganizationalUnitIdentifiers,内部是通过设置的证书拿到证书链,然后取摘要,保存到为“COP”为key的ouIdentifiers中。
- 有兴趣的可以去查下这里是用来干什么的,再仔细体会下。
- 之前的证书只是校验是不是属于这个公司,只需要看他的签发单位是根ca或中间ca就够了。而部门的验证不能这样粗糙,起码要证明是指定的证书来签发的。
- 其次所有属于这个部门的成员,必须要由配置文件指定的证书来签发。
- 而这里的实现是通过取待认证节点的签发单位的证书链摘要,来去msp里的“COP”的ouIdentifiers里查询,看是否已经包含了,如果能找到,说明这个节点确定是这个部门的成员。
- 想象下,如果从cacert.pem再往下签发一个中间ca证书,来签发部门成员行不行?显然不行,按照上面的实现,是不属于该部门,除非你在配置文件里面明确设置。
setupNodeOUs
func (msp *bccspmsp) setupNodeOUs(config *m.FabricMSPConfig) error {
if config.FabricNodeOus != nil {
msp.ouEnforcement = config.FabricNodeOus.Enable
// ClientOU
msp.clientOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.ClientOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.ClientOuIdentifier.Certificate)
if err != nil {
return err
}
msp.clientOU.CertifiersIdentifier = certifiersIdentifier
}
// PeerOU
msp.peerOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.PeerOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.PeerOuIdentifier.Certificate)
if err != nil {
return err
}
msp.peerOU.CertifiersIdentifier = certifiersIdentifier
}
} else {
msp.ouEnforcement = false
}
return nil
}
这里跟上面Organizational Units的处理类似,上面称之为组织分类,而这里是身份分类。这里我们看下配置文件。
也就是说成员证书设置为peer或client,且证书链摘要能被msp承认,那么就会被msp识别为你所声明的身份。
NodeOUs: Enable: true ClientOUIdentifier: Certificate: "cacerts/cacert.pem" OrganizationalUnitIdentifier: "client" PeerOUIdentifier: Certificate: "cacerts/cacert.pem" OrganizationalUnitIdentifier: "peer"
postSetup
func (msp *bccspmsp) postSetupV11(conf *m.FabricMSPConfig) error {
// Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required. Call post setup as per V1
return msp.postSetupV1(conf)
}
// Check that admins are clients
principalBytes, err := proto.Marshal(&m.MSPRole{Role: m.MSPRole_CLIENT, MspIdentifier: msp.name})
if err != nil {
return errors.Wrapf(err, "failed creating MSPRole_CLIENT")
}
principal := &m.MSPPrincipal{
PrincipalClassification: m.MSPPrincipal_ROLE,
Principal: principalBytes}
for i, admin := range msp.admins {
err = admin.SatisfiesPrincipal(principal)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("admin %d is invalid", i))
}
}
return nil
}
如果设置了config.yaml的NodeOUs为false,那么只需要验证admin的证书是否是ca签发的即可
如果是false,那需要判断,admin的签发单位身份是否是client,至于为什么这么设计,难道peer就不能成为admin?我还没领会到精髓。不过我找到官方文档的一段话。
When the classification is enabled, MSP administrators need to be clients of that MSP, meaning that their x509 certificates need to carry the OU that identifies the clients.
小结
- 至此,整个MSP安装结束,可以看到其实还是很讲究的,知道了这些,之后校验的实现会看得很快了。