以前的证书都是由cryptogen这个工具根据crypto-config.yaml而生成的。但是在实际生产环境中肯定不能这么做,我们应该为每个Org建立一个CA,由CA来管理其中的用户。下面我们就试着讲Fabric CA集成到整个Fabric网络中,并用CA Client生成新用户,最终使用新用户调用ChainCode,验证新用户的合法性。仍然以官方fabric1.0.的e2e_cli为例。
1.修改docker-compose文件,增加CA容器
以给org1这个组织增加CA容器为例,打开e2e_cli文件夹中的docker-compose-cli.yaml ,增加以下内容:
ca0:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca0
- FABRIC_CA_SERVER_TLS_ENABLED=false
ports:
- "7054:7054"
command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/${PRIVATE_KEY} -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca0
这里我们注意到,Fabric CA Server启动的时候,带了3个重要的参数:ca.certfile 指定了CA的根证书,ca.keyfile 指定了接下来给新用户签发证书时的私钥,这里我们使用变量${PRIVATE_KEY}代替,这是因为每次network_setup的时候,私钥的名字是不一样的,所以需要从启动脚本中传入。另外就是-b参数,指定了CA Client连接CA Server时使用的用户名密码。
2.修改network_setup.sh启动脚本,将CA容器启动的参数带入
接下来我们需要修改network_setup.sh文件,因为前面我们使用了变量${PRIVATE_KEY},所以这里我们需要读取变量并带入docker-compose 启动的时候。具体修改如下:
function networkUp () {
if [ -f "./crypto-config" ]; then
echo "crypto-config directory already exists."
else
#Generate all the artifacts that includes org certs, orderer genesis block,
# channel configuration transaction
source generateArtifacts.sh $CH_NAME
fi
#以下添加
folder="crypto-config/peerOrganizations/org1.example.com/ca"
privName=""
for file_a in ${folder}/*
do
temp_file=`basename $file_a`
if [ ${temp_file##*.} != "pem" ];then
privName=$temp_file
fi
done
echo $privName
#以上为添加
if [ "${IF_COUCHDB}" == "couchdb" ]; then
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
else
#PRIVATE_KEY=$privName为添加的
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT PRIVATE_KEY=$privName docker-compose -f $COMPOSE_FILE up -d 2>&1
fi
if [ $? -ne 0 ]; then
echo "ERROR !!!! Unable to pull the images "
exit 1
fi
docker logs -f cli
}
这里脚本的逻辑很简单,就是去crypto-config/peerOrganizations/org1.example.com/ca这个文件夹中去遍历文件,找到私钥文件的文件名,并把文件名赋值给privName,然后在docker-compse的启动时,指定到PRIVATE_KEY即可。
3.使用CA Client生成新用户
只需要经过前面2步,我们给Org1设置的CA Server就算完成了。
3.1启动Fabric网络
./network_setup.sh up
看一下容器启动的情况,ca容器有没有启动就挂掉,没有的话可以直接查看ca容器的日志了。
wang@wang:~$ docker logs -f 5d31cdc1ed0d
2019/08/22 08:36:29 [DEBUG] Home directory: /etc/hyperledger/fabric-ca-server
2019/08/22 08:36:29 [DEBUG] parent server URL: ''
2019/08/22 08:36:29 [INFO] Created default configuration file at /etc/hyperledger/fabric-ca-server/fabric-ca-server-config.yaml
2019/08/22 08:36:29 [INFO] Starting server in home directory: /etc/hyperledger/fabric-ca-server
。。。
3.2下载并安装Fabric CA Client(见前面文章,有详细介绍,包括可能的出错。)
官方提供的CA Client需要依赖于libtool这个库,所以需要先安装这个库,运行命令:
sudo apt install libtool libltdl-dev
然后执行以下命令安装Fabric CA Client:
go get -u github.com/hyperledger/fabric-ca/cmd/…
该命令执行完毕后,我们应该在~/go/bin下面看到生成的2个文件:
fabric-ca-client fabric-ca-server
3.3注册认证管理员
我们首先需要以管理员身份使用CA Client连接到CA Server,并生成相应的文件。
wang@wang:~$ export FABRIC_CA_CLIENT_HOME=$HOME/ca
wang@wang:~$ fabric-ca-client enroll -u http://admin:adminpw@localhost:7054
2019/08/22 16:45:01 [INFO] generating key: &{A:ecdsa S:256}
2019/08/22 16:45:01 [INFO] encoded CSR
2019/08/22 16:45:01 [INFO] Stored client certificate at /home/wang/ca/msp/signcerts/cert.pem
2019/08/22 16:45:01 [INFO] Stored root CA certificate at /home/wang/ca/msp/cacerts/localhost-7054.pem
2019/08/22 16:45:01 [INFO] Stored Issuer public key at /home/wang/ca/msp/IssuerPublicKey
2019/08/22 16:45:01 [INFO] Stored Issuer revocation public key at /home/wang/ca/msp/IssuerRevocationPublicKey
这个时候我们可以去$HOME/ca目录,看到CA Client创建了一个fabric-ca-client-config.yaml文件和一个msp文件夹。config可以去修改一些组织信息之类的。
3.4注册新用户
接下来我们想新建一个叫devin的用户,那么需要先执行这个命令:
wang@wang:~$ fabric-ca-client register --id.name devin --id.type user --id.affiliation org1.department1 --id.attrs 'hf.Revoker=true,foo=bar'
2019/08/22 16:45:50 [INFO] Configuration file location: /home/wang/ca/fabric-ca-client-config.yaml
Password: pITZNUwWKDJG
系统会返回一个该用户的密码,我们拿到这个密码以后就可以再次使用enroll命令,给devin这个用户生成msp的私钥和证书:
wang@wang:~$ fabric-ca-client enroll -u http://devin:pITZNUwWKDJG@localhost:7054 -M $FABRIC_CA_CLIENT_HOME/devinmsp
2019/08/22 16:46:18 [INFO] generating key: &{A:ecdsa S:256}
2019/08/22 16:46:18 [INFO] encoded CSR
2019/08/22 16:46:18 [INFO] Stored client certificate at /home/wang/ca/devinmsp/signcerts/cert.pem
2019/08/22 16:46:18 [INFO] Stored root CA certificate at /home/wang/ca/devinmsp/cacerts/localhost-7054.pem
2019/08/22 16:46:18 [INFO] Stored Issuer public key at /home/wang/ca/devinmsp/IssuerPublicKey
2019/08/22 16:46:18 [INFO] Stored Issuer revocation public key at /home/wang/ca/devinmsp/IssuerRevocationPublicKey
现在新用户devin的私钥和证书就在$HOME/ca/devinmsp目录下,我们可以使用tree命令查看一下:
wang@wang:~/ca/devinmsp$ tree
.
├── cacerts
│ └── localhost-7054.pem
├── IssuerPublicKey
├── IssuerRevocationPublicKey
├── keystore
│ ├── 262d8157d1fc38fdd6ca3acb7d662eba49120d2a563b6b75228aefa7ad80005e_sk
│ └── d3595021e666ddb8ba409a1abf144544af909b8e488f88a17e0a203acdff98b7_sk
├── signcerts
│ └── cert.pem
└── user
4 directories, 6 files
4.编写ChainCode验证当前用户
由于官方提供的example02并没有关于当前用户的信息的代码,所以我们需要编写自己的ChainCode。
这里我们主要是用到ChainCode接口提供的GetCreator方法,具体完整的ChainCode如下:
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
"fmt"
"encoding/pem"
"crypto/x509"
"bytes"
)
type SimpleChaincode struct {
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
fmt.Println("invoke is running " + function)
if function == "cert" {//自定义函数名称
return t.testCertificate(stub, args)//定义调用的函数
}
return shim.Error("Received unknown function invocation")
}
func (t *SimpleChaincode) testCertificate(stub shim.ChaincodeStubInterface, args []string) pb.Response{
creatorByte,_:= stub.GetCreator()
certStart := bytes.IndexAny(creatorByte, "-----")// Devin:I don't know why sometimes -----BEGIN is invalid, so I use -----
if certStart == -1 {
fmt.Errorf("No certificate found")
}
certText := creatorByte[certStart:]
bl, _ := pem.Decode(certText)
if bl == nil {
fmt.Errorf("Could not decode the PEM structure")
}
fmt.Println(string(certText))
cert, err := x509.ParseCertificate(bl.Bytes)
if err != nil {
fmt.Errorf("ParseCertificate failed")
}
fmt.Println(cert)
uname:=cert.Subject.CommonName
fmt.Println("Name:"+uname)
return shim.Success([]byte("Called testCertificate "+uname))
}
我们只需要在~/go/src/github.com/hyperledger/fabric/examples/chaincode/go目录下新建一个文件夹,比如test1,然后新建一个文件test1.go并粘贴上面的代码进去即可。
现在ChainCode已经开发完成,我们需要部署并测试该ChainCode的正确性,下面是部署步骤:
wang@wang:~$ docker exec -it cli bash
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n test1 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/test1
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n test1 -v 1.0 -c '{"Args":[]}'
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n test1 -c '{"Args":["cert"]}'
详细日志如下:
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n test1 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/test1
2019-08-22 08:47:15.569 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2019-08-22 08:47:15.570 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2019-08-22 08:47:15.570 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2019-08-22 08:47:15.570 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2019-08-22 08:47:15.690 UTC [golang-platform] getCodeFromFS -> DEBU 005 getCodeFromFS github.com/hyperledger/fabric/examples/chaincode/go/test1
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 006 Discarding GOROOT package bytes
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 007 Discarding GOROOT package crypto/x509
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 008 Discarding GOROOT package encoding/pem
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 009 Discarding GOROOT package fmt
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 00a Discarding provided package github.com/hyperledger/fabric/core/chaincode/shim
2019-08-22 08:47:16.146 UTC [golang-platform] func1 -> DEBU 00b Discarding provided package github.com/hyperledger/fabric/protos/peer
2019-08-22 08:47:16.147 UTC [golang-platform] GetDeploymentPayload -> DEBU 00c done
2019-08-22 08:47:16.153 UTC [msp/identity] Sign -> DEBU 00d Sign: plaintext: 0A89070A5B08031A0B0894ADF9EA0510...DB607F060000FFFFE9B34D97000E0000
2019-08-22 08:47:16.153 UTC [msp/identity] Sign -> DEBU 00e Sign: digest: EDCF0D63D84869FA1FB497C04AB61A81148C6FC27ACDE1594D1CFC3A20C619F6
2019-08-22 08:47:16.167 UTC [chaincodeCmd] install -> DEBU 00f Installed remotely response:
2019-08-22 08:47:16.167 UTC [main] main -> INFO 010 Exiting.....
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n test1 -v 1.0 -c '{"Args":[]}'
2019-08-22 08:47:30.480 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2019-08-22 08:47:30.480 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2019-08-22 08:47:30.487 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2019-08-22 08:47:30.487 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2019-08-22 08:47:30.489 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A95070A6708031A0C08A2ADF9EA0510...1A000A000A04657363630A0476736363
2019-08-22 08:47:30.490 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: 747D8129156E804468347BF9EBA53BB18C6D73BA9C7668177664ABE7D068D4D7
2019-08-22 08:47:49.148 UTC [msp/identity] Sign -> DEBU 007 Sign: plaintext: 0A95070A6708031A0C08A2ADF9EA0510...19E30C4760431BF8C018AF59F4076064
2019-08-22 08:47:49.148 UTC [msp/identity] Sign -> DEBU 008 Sign: digest: 7C87066A329D852D87045E0DD9BA6CA7AFB20A87ACA3B45D42DF18F43F3C6F87
2019-08-22 08:47:49.155 UTC [main] main -> INFO 009 Exiting.....
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n test1 -c '{"Args":["cert"]}'
2019-08-22 08:47:53.631 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2019-08-22 08:47:53.631 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2019-08-22 08:47:53.631 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2019-08-22 08:47:53.631 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2019-08-22 08:47:53.631 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A96070A6808031A0C08B9ADF9EA0510...07120574657374311A060A0463657274
2019-08-22 08:47:53.631 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: 33D4023FD656DADA533F19421ADAC329873A1473326D102CCC5AA4A87ED701E7
Query Result: Called testCertificate [email protected]
2019-08-22 08:47:53.655 UTC [main] main -> INFO 007 Exiting.....
系统返回结果,说明我们当前的用户是[email protected]
5.设置新用户的证书和私钥文件夹,验证新用户的可用性
因为我们是给org1设置的CA,用户devin也是在org1下,所以需要把~/ca/devinmsp下面的文件转移到org1下面。org1的用户证书和私钥文件夹在:
~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users
我们需要新建文件夹devin用于保存新用户的证书和私钥,我们新建一个Ubuntu的命令行窗口,前面已经登录您的cli的窗口保留,我们接下来还会用。
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ ls
[email protected] [email protected]
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ mkdir devin
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ cp ~/ca/devinmsp/ devin/msp -R
不知道什么原因,Fabric在使用的时候需要用到msp文件夹下的admincerts文件夹,但是CA Client在生成的时候并没有这个文件夹,所以我们需要从signcerts这个文件夹中拷贝一个过来,运行以下命令:
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ mkdir devin/msp/admincerts
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ cp devin/msp/signcerts/cert.pem devin/
msp/admincerts/
好现在我们的新用户的所有证书准备完毕,tree devin看看结果
wang@wang:~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/crypto-config/peerOrganizations/org1.example.com/users$ tree devin/
devin/
└── msp
├── admincerts
│ └── cert.pem
├── cacerts
│ └── localhost-7054.pem
├── IssuerPublicKey
├── IssuerRevocationPublicKey
├── keystore
│ ├── 262d8157d1fc38fdd6ca3acb7d662eba49120d2a563b6b75228aefa7ad80005e_sk
│ └── d3595021e666ddb8ba409a1abf144544af909b8e488f88a17e0a203acdff98b7_sk
├── signcerts
│ └── cert.pem
└── user
6 directories, 7 files
接下来切换到cli窗口,我们把当前cli的用户msp文件夹切换成devin的文件夹,具体命令是:
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/devin/msp
现在我们再来运行一下ChainCode:
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n test1 -c '{"Args":["cert"]}'
2019-08-22 08:49:59.789 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2019-08-22 08:49:59.789 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2019-08-22 08:49:59.790 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2019-08-22 08:49:59.790 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2019-08-22 08:49:59.790 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A96090A6808031A0C08B7AEF9EA0510...07120574657374311A060A0463657274
2019-08-22 08:49:59.790 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: E28D1FED185D06497E0A1A5F062D5419F19E7DE12D48E4FAE3431176D6E5E91C
Query Result: Called testCertificate devin
2019-08-22 08:49:59.814 UTC [main] main -> INFO 007 Exiting.....
root@2569abb9fb81:/opt/gopath/src/github.com/hyperledger/fabric/peer#
用户已经由Admin变成了devin。
可以看下开始监控的ca容器日志,看看打印了什么。
以上就是关于Fabric CA环境集成的简单测试。关于CA Server有配置文件在CA Server容器内部,可以针对不同的org信息进行修改。而CA Client也有配置文件,也可以在enroll之前进行修改。关于具体的修改方法,参考官方文档:http://hyperledger-fabric-ca.readthedocs.io/en/latest/
实践过程中可能出现的报错:
(1)执行fabric-ca-client enroll -u http://admin:adminpw@localhost:7054的时候报连接7054端口的时候permission denied,因为ca容器启动之后执行这条命令会报错:Error: Validation of certificate and key failed: Invalid certificate and/or key in files ‘/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem’ and ‘/etc/hyperledger/fabric-ca-server-config/c4c436af5ace96a363cc2ae7d8856abeb29411acca4e537b87aab468ace77b1e_sk’: Public key and private key do not match。
可能因为/crypto-config/peerOrganizations/org1.example.com/ca/下有很多的私钥,原因就是没有执行./network down进行清理。
(2)Error: Error endorsing query: rpc error: code = Unknown desc = Failed to deserialize creator identity, err The supplied identity is not valid, Verify() returned x509: certificate signed by unknown authority -
可能是docker-compose-cli.yaml中的ca server容器的command一行可能有错误,比如是简单的command: sh -c ‘fabric-ca-server start -b admin:adminpw’