节点准入机制
由于使用CA流程需要特定的工具去生成证书以及颁发证书,流程比较复杂。前期打算用节点公私钥进行验证。假设此时已经搭建完第一个主节点A,此时B节点待加入。大概流程如下:
- 节点A的信息会写入创世区块。我们对第一个创建的主节点,不允许删除,不允许更新。
- 在链A上将A节点信息以及B节点信息写入系统合约。节点信息大概有节点IP,节点p2p通讯端口,以及节点的公钥。此公钥会跟节点IP进行绑定。
- 启动节点B。此时,节点B信息已在节点A的系统合约上存在,A会主动去链接B。而B的创世区块有节点A的信息,也会主动去链接B。假设A先主动去链接B。
- 接下来是互相验证。那么A如何验证B节点呢?A节点会生成一个32位的随机数发送给B,B用它的私钥对这个32位的随机数进行签名生成一个65位的签名数据然后发给A。A收到这个数之后,用之前生成的32位随机数加上这个B签名的数据能解出一个公钥。然后用这个公钥跟系统合约里面存储的节点B的公钥一比较是否一致,如果一致,则验证通过。为什么A节点要随机生成一个32位的随机数给B去签名呢?因为如果用固定的数据让B去签名,那么作恶节点截获到这个数据之后,可以用这段数据给A去验证,A解出的也是B的公钥,所以给B签名的数据A每次都要随机生成。至于B验证A,也是同样的步骤。
下面是节点互相验证的部分Go代码。
发起链接方
// verification remote id
// send rand 32 bytes to remote id
msgHash := make([]byte, 32)
rand.Read(msgHash)
if _, err = conn.Write(msgHash); err != nil {
log.Warn("initiatorEncHandshake send msgHash fail")
return s, err
}
// receive sign(rand 65 bytes) by remote private key
signature := make([]byte, 65)
if _, err := io.ReadFull(conn, signature); err != nil {
log.Warn("initiatorEncHandshake receive msgSig error: ", err)
return s, err
}
// checkout
recoveredPub, _ := crypto.Ecrecover(msgHash, signature)
remotePubStr := hex.EncodeToString(recoveredPub[1:])
if remoteID.String() != remotePubStr {
log.Warn("initiatorEncHandshake signature fail, cur remoteId: ", remoteID.String(), ". recove remoteId: ", remotePubStr)
return s, err
}
// prove myself
if _, err := io.ReadFull(conn, msgHash); err != nil {
log.Warn("receiverEncHandshake receive msgHash error: ", err)
return s, err
}
signature, _ = crypto.Sign(msgHash, prv)
if _, err = conn.Write(signature); err != nil {
log.Warn("receiverEncHandshake send sign data fail")
return s, err
}
接收方
// prove myself
msgHash := make([]byte, 32)
if _, err := io.ReadFull(conn, msgHash); err != nil {
log.Warn("receiverEncHandshake receive msgHash error: ", err)
return s, err
}
signature, _ := crypto.Sign(msgHash, prv)
if _, err = conn.Write(signature); err != nil {
log.Warn("receiverEncHandshake send sign data fail")
return s, err
}
// verification remote public key is in node list
rand.Read(msgHash)
if _, err = conn.Write(msgHash); err != nil {
log.Warn("receiverEncHandshake send msgHash fail")
return s, err
}
signature = make([]byte, 65)
if _, err := io.ReadFull(conn, signature); err != nil {
log.Warn("receiverEncHandshake receive msgSig error: ", err)
return s, err
}
recoveredPub, err := crypto.Ecrecover(msgHash, signature)
if err != nil {
log.Warn("receiverEncHandshake ecrecover publickey error ", err)
return s, err
}
pubStr := hex.EncodeToString(recoveredPub[1:])
validNode := (pubStr == GetRootNode().ID.String()) // root node is forbid delete!
if !validNode {
if common.SysCfg.IsValidJoinNode(pubStr) {
validNode = true
}
}
log.Info("PeerInfo","pubStr", pubStr, "validNode", validNode)
if(!validNode) {
log.Warn("joined node is a deleted node ", "pubStr", pubStr)
return s, errors.New("join node is a invalid node")
}