Overview
In the previous article I have described how identity is implemented in Hyperledger Fabric and shown how to access control inside chaincode based on the identity (digital certificate). In this article we will continue our journey.
We first take a look on the cryptogen
tool, and what are generated with a proper configuration file. Instead of generating the required amount of user certificates with cryptogen
, a better way is to use Fabric-CA. We take a bit deep-dive into how the Fabric-CA is configured, and then issue a user certificate through fabric SDK. Finally, we again use Attribute-based Access Control (ABAC) to limit only those entity with proper attribute can invoke some part of chaincode.
Setup a New Network
Let’s first setup a directory based on the configuration files in Basic Network. We name the directory myNetwork.
cd fabric-samples
mkdir myNetwork
cd myNetwork
Here I put it under fabric-samples. The commands below and in docker-compose file later will match this directory design. In case you are using another directory, please make change accordingly.
First we copy the most important three files from Basic Network.
cp ../basic-network/crypto-config.yaml .
cp ../basic-network/configtx.yaml .
cp ../basic-network/docker-compose.yml .
We will first work on crypto-config.yaml
.
Use cryptogen
to Generate Crypto Material
Configuration File crypto-config.yaml
Here is what we see the configuration file from the Basic Network setup: one orderer organization and one peer organization Org1, with one peer node defined in Org1.
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Org1
Domain: org1.example.com
Template:
Count: 1
Users:
Count: 1
When we generate the crypto material based on this configuration file,
../bin/cryptogen generate --config=./crypto-config.yaml
We will have a directory structure under crypto-config directory.
Let’s focus on Peers Organization. In fact Orderer Organization looks almost the same but simpler.
Relate the crypto-config.yaml file to the directory structure
All peer-related material is under peersOrganizations directory. In our setup we only have one organization, org1.example.com. If we have more organizations defined under PeerOrgs in configuration file, they will appear as different directories.
Inside org1.example.com, we have several directories. The ones we are interested in are ca, users and peers.
In ca directory, it is where the root certificate and signing key of the Certificate Authority (CA) for org1.example.com. All certificates inside org1.example.com are issued and signed by this CA.
In users directory, it contains all users under org1.example.com. The first one Admin@org1.example.com is created by system. We see there is another User1@org1.example.com. It is created when we specify Users: Count: 1
in the configuration file. If we need more, we just change the number and more users will be created. Inside Admin and the users, there is a standard msp directory.
In peers directory, it contains all the peer node under org1.example.com. The number of peers created is specified in the Template: Count: 1
in the configuration file. Change this number if we need more peer node for Org1. The peer node is designated as peern.org1.example.com, where n is 0, 1, etc. Inside each peer node, there is a standard msp directory.
Finally, msp directory under peers or users has a similar directory structure. Here are the directories we are interested in,
- cacerts: the CA certificate of org1.example.com, that is, ca.org1.example.com-cert.pem.
- keystore: the signkey of the entity (user or peer node)
- signcerts: the certificate of the entity (user or peer node)
Certificates at a Glance
We take a look on those certificates using openssl.
ca.org1.example.com, which is a self-sign certificate, serving as the root CA in org1.example.com.
Admin@org1.example.com-cert.pem: the certificate of Admin, issued to Admin and signed by the CA above.
User1@org1.example.com-cert.pem: similar to the one for Admin.
And finally peer0.org1.example.com-cert.pem: it is the certificate of peer0. Again it is issued and signed by CA.
Revisit cryptogen
From the above we observe that the tool cryptogen
has created the required crypto material based on the configuration file specified.
If we zoom in an organization, cryptogen
has created a self-signed certificate, acting as the certificate authority of the organization which issues digital certificate to all entities, including peer nodes and users, defined in the organization.
When we take a look on the cryptogen
tool, we see several options. Besides the generate command, we can also use showtemplate to show the default template, and see how we can construct the configuration file to meet our requirement (e.g. change the Issuer of CA, add Subject Alternative Names , etc.)
While we use cryptogen
to generate the crypto material for our network, it is only good for a demo setup. In real production it is not the desired way, because
- the tool should not be run by any except authorized personnel,
- the tool may be able to add more users but the process is not clear, and
- the tool lacks the flexibility to add attributes on user certificates, which may be useful when we apply attribute-based access control (see later).
In such a case, a better approach is to have a Certificate Authority which can issue certificates after the initial cryptogen
is complete. Here Fabric-CA plays this role.
Fabric-CA
Overview
Fabric-CA is the Certificate Authority tool developed by Hyperledger Fabric serving a CA role. Once up and running, Fabric-CA can issue new certificates with specific requirement on request. Fabric-CA can accessed using Fabric-CA Client or Fabric SDK, both from Hyperledger Fabric as well.
Note that use of Fabric-CA is optional. If there is no need to add more user certificates for a fabric network, you do not need Fabric-CA. You simply use cryptogen
mentioned above to generate the required amount of user certificates, and then distribute the certificates to the right entities.
Or if your company already has a CA server established, you can use your own CA server instead. The “how-to” incorporate your enterprise CA into Hyperledger Fabric is beyond the scope of this article. We simply use cryptogen
first and establish a Fabric-CA server with the crypto material.
Examine Fabric-CA Setup in Basic Network
As we mentioned in last article, Basic-Network in fabric-sample comes with a Fabric-CA container. The setup is inside the docker-compose file.
If we take a look on the ca portion basic-network/docker-compose.yml, here we see
ca.example.com:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca.example.com
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/4239aa0dcd76daeeb8ba0cda701851d14504d31aad1b2ddddbac6a57365e497c_sk
ports:
- "7054:7054"
command: sh -c 'fabric-ca-server start -b admin:adminpw'
volumes:
- ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca.example.com
networks:
- basic
Here is the observation
- After we run docker-compose with this file, a ca.example.com container running fabric-ca image is instantiated.
- A local directory
./crypto-config/peerOrganizations/org1.example.com/ca
is mapped to the directory/etc/hyperledger/fabric-ca-server-config
. The directory is holding the ca signing key and signing certificate of the CA for Org1 (see directory structure discussion in the session above). - And these two files are defined in environment variables:
FABRIC_CA_SERVER_CA_CERTFILE
andFABRIC_CA_SERVER_CA_KEYFILE
. The crypto material generated bycryptogen
is now configured in the Fabric-CA. - ca.example.com exposes port 7054 for accessing.
- Finally the command to star the Fabric-CA is sh -c ‘fabric-ca-server start -b admin:adminpw’. The -b is the bootstrap identity. We will use it when we enrol the admin and users later.
With this setup, Fabric-CA is running. Digital certificates can be issued to a new entity later.
Bring Up our Setup
Let’s continue with our workspace.
And in order to bring up our network, we need to generate the correct configuration transaction first.
// inside fabric-sample/myNetworkexport FABRIC_CFG_PATH=$PWDmkdir config../bin/configtxgen -profile OneOrgOrdererGenesis -outputBlock ./config/genesis.block../bin/configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID mychannel
We will see the two files: genesis.block and channel.tx. They are good enough for our network.
Finally modify the docker-compose.yml to reflect the CA we just generate. In particular, update the signing key variable to use our own signing key. Here is the signing key of CA.
Update the environment variable in docker-compose file
FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/7bb3855c7cb680de8b1482a413e8e16be2fcea544ea395be6dd86171dbe9d067_sk
Also a minor modification for our demonstration purpose
- inside the docker-compose.yml, uncomment the cli service “depends_on:” and “peer0.org1.example.com” and “couchdb”
- copy the .env from Basic Network: cp ../basic-network/.env .
With this, we can bring up the containers.
docker-compose -f docker-compose.yml up -d
And we see the five containers are brought up and running.
Observe Fabric-CA Container
Now we can take a look what is inside the ca.example.com.
docker exec -it ca.example.com bash
We first take a look on the command running:
And the environment variables
This Fabric-CA is up and running, able to issue certificates.
Issue User Certificates
As mentioned above, there are two ways to use the Fabric-CA. In this example, we are demonstrating the use the Javascript SDK.
Instead of writing from scratch, we again leverage some existing examples in fabric-samples. The FabCar application (fabric-samples/fabcar/javascript) comes with two client application: enrollAdmin.js and registerUser.js. These applications use Fabric SDK. These files are for basic network and we modify them to fit our running CA.
We begin with a copy of connection.json from basic-network to myNetwork. It contains the information our client application will use later.
cd fabric-samples
cp basic-network/connection.json myNetwork/
Then we create a new directory: myApp inside fabric-samples.
cd fabric-samples
mkdir myApp
cd myApp
We will initialize our node environment and install the required SDK.
npm init // accept all default
npm install fabric-ca-client — save
npm install fabric-network — save
Now we copy the two files from fabcar/javascript directory.
cp ../fabcar/javascript/enrollAdmin.js .
cp ../fabcar/javascript/registerUser.js .
We first modify the two applications to reflect our directory. In both files, look for the line of const ccpPath
, and change it to our directory.
const ccpPath = path.resolve(__dirname, '..', 'myNetwork', 'connection.json');
Now let’s take a quick look on these two applications and see how it works.
Now let’s take a quick look on these two applications and see how it works.
enrollAdmin.js: this application is to enrol an administrator on our CA. Here is the logic.
- Load the required modules from fabric-ca-client and fabric-network.
- Retrieve the detail about the myNetwork deployment. It is used for obtaining the access point of Fabric CA.
- Check whether “admin” is already inside the wallet directory. If so, no further action is needed.
- Enrol the admin user to the Fabric CA with enrollment ID and Secret (aligned with what is defined in Fabric CA, see ca.example.com service in the docker-compose file myNetwork/docker-compose.yml).
- The result of enrollment is key pair and certificate. The result is then stored in
wallet/admin/
.
registerUser.js: the application is to let admin register and enrol a user. Here is the logic.
- Load the required modules from fabric-network.
- Retrieve the detail about the myNetwork deployment.
- Check whether user1 is already enrolled. If so no further action is needed.
- Check whether admin is here. If admin is not yet enrolled, prompt for enrollAdmin.js and no further action is needed.
- Create a new gateway connecting to peer, with connection detail from myNetwork deployment.
- Register the user1 with admin created in previous part. The result is a secret.
- Now we can enrol the user1 with the secret.
- The result of enrollment is key pair and certificate for user1. The result is then stored in
wallet/user1/
.
To enroll the admin
node enrollAdmin.js
We will see wallet/admin
directory, and the certificate is inside the file admin.
Now admin registers user1, and enrol user1
node registerUser.js
The JSON files (admin or user) are the format the SDK can use later. If you are developing client application, you can read this JSON file. In our demonstration, we are using CLI to interact with chaincode later. We need to extract the certificate and signing key in later part of session.
Finally, we will take a look on the certificate inside the JSON files. I extract them and create a PEM file before reading them into detail.
The admin certificate
The user1 certificate
We see that both certificates are issued by the same CA. Besides, the subjects on both certificates reflects what is configured in the client applications.
In particular, there is an entry of attributes in the user1 certificate. Three attributes are defined:
- hf.Affiliation: org1.department1
- hf.EnrollmentID: user1
- hf.Type: client
The information comes from the code in registerUser.js. We can specify the required attributes when register a user.
const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'user1', role: 'client' }, adminIdentity);const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: secret });
These two lines of code represents the two steps:
- When the CA registers user1, it has specified the affiliation, enrollment id and the role. The result is a secret for user1. This information is stored inside CA.
- Using this secret, CA enrols user1. From the secret specified, CA will prepare the certificate for user1, and inside which the attributes mentioned above are included with the value specified in the step 1 (registration).
Complete the Setup with Channel Creation
So far we only bring up all the required containers for myNetwork. Before we move forward into the chaincode portion, we need to create the channel mychannel first and make peer0.org1.example.com join the channel. Here are the commands.
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.txdocker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block
Attribute Based Access Control (ABAC)
Finally we reach the topic we promise in last article. ABAC provides a chaincode level access control by examining the digital certificate of transaction requester (invoker). The Client Identity Chaincode Library provides the methods to obtain useful information from the digital certificate.
In the previous article, we have shown how to get the subject in X509 certificate and check whether it is a specific user. This time we will inspect the attributes of a digital certificate and again make sure the certificate contains a specific attribute and a value before the transaction is being accepted and processed by the fabric network.
Prepare the Chaincode SACC-ABAC
We use the same code in previous article for demonstration. But this time, the checking logic is not on the Subject of invoker’s Digital Certificate. Instead, we will check if the attribute hf.Affiliation has a value org1.department1 or not.
First we create another chaincode sacc-abac
cd fabric-samples/chaincode
cp -r abac/ sacc-abac/
cp sacc/sacc.go sacc-abac/go/sacc-abac.go
cd sacc-abac/go
rm abac.go
Now modify the two parts in sacc-abac.go.
import portion
import ("fmt""github.com/hyperledger/fabric/core/chaincode/shim/ext/cid"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
set() portion
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
// only user with org1.department1 affiliation can set the value
department1err := cid.AssertAttributeValue(stub, "hf.Affiliation", "org1.department1")
if department1err != nil {
return "", fmt.Errorf("Only Department 1 can set value.")
} ...
original code
...}
Now we install and instantiate the chaincode. It is done in CLI, and by default by Admin@org1.example.com.
docker exec cli peer chaincode install -n sacc -v 0 -p github.com/sacc-abac/godocker exec cli peer chaincode instantiate -n sacc -v 0 -C mychannel -c '{"Args":["a", "100"]}'
Create a New User for Chaincode Access
Remember we have already created two users using cryptogen
tool? They are Admin@org1.example.com and User1@org1.example.com. Both do not have the affiliation attribute set. We will create one new user, called user1fromCA, with proper msp structure such that CLI can use it when invoking in chaincode.
If we take a look on the volume mapping on CLI again (docker-compose.yml), we see the crypto-config directory in our local host is mapped to crypto inside CLI. Therefore, the easiest way is to create one more user user1fromCA manually, with proper msp structure and content.
cd myNetwork/crypto-config/peerOrganizations/org1.example.com/users/
cp -r User1@org1.example.com/ user1fromCA
Now we have user1fromCA directory structure. The components we need to modify are
- msp/admincerts: change it to the certificate in
wallet/user1/user1
in PEM format - msp/signcerts: change it to our certificate in
wallet/user1/user1
in PEM format (same as admincerts) - msp/keystore: change it to
wallet/user1/xxx-priv
Invoke Chaincode
We first get value from chaincode. As there is no access control, we can get it by any user.
docker exec cli peer chaincode invoke -n sacc -C mychannel -c '{"Args":["get", "a"]}'
But when we try to set a value, and using the default Admin@org1.example.com in CLI, we will get error.
docker exec cli peer chaincode invoke -n sacc -C mychannel -c '{"Args":["set", "a", "500"]}'
And we see the error message we defined in the chaincode.
Finally we are using user1fromCA to set the value.
docker exec -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/user1fromCA/msp" cli peer chaincode invoke -n sacc -C mychannel -c '{"Args":["set", "a", "500"]}'
The update is successful.
Summary
We have walked through both the cryptogen
tool and the Fabric-CA, and see the difference between them. Cryptogen
is good for demo or static setup, while Fabric-CA provides a better way to issue certificates in real life. Also, with Fabric-CA we can issue certificate with specific attributes and values. These can be used in chaincode access control with attribute-based access control (ABAC) capability in Hyperledger Fabric.
Hope you find this and previous article helpful.