Varun Agarwal
Varun Agarwal

Reputation: 1587

Identity management on Hyperledger Fabric

I am using Hyperledger fabric 1.4.1 and the latest versions of fabric-contract-api for the smart contracts, fabric-client for the low level APIs to manage channel creation and chaincodes, and fabric-network for interaction with the peers. I have referred to this sample for setting up my network.

I have written a basic chaincode package in nodejs and setup a simple 1 org, 1 peer, 1 orderer network. The first step is to connect to the peer node and use the fabric-ca-client to create the admin identity. As per examples the enrollment id and secret used is admin, adminpw. This is dependent on the configuration used here.

The code I use to create and join a channel, followed by installing and instantiating a chaincode is

const CAClient = require('fabric-ca-client');
const client = require('fabric-client');
const User = client.User;
const fs = require('fs');
const path = require('path');
const basePath = path.resolve(__dirname, '../certs');
const readCryptoFile = filename => fs.readFileSync(path.resolve(basePath, filename)).toString();
const ccPath = path.resolve(__dirname, '../chaincode/src/ax-chaincode');
const url = require('url');
const http = require('http');

  let myClient = new client();
  const ordererConfig = {
    hostname: 'orderer0',
    url: 'grpc://localhost:7050',
    pem: readCryptoFile('ordererOrg.pem')
  };
  const orderer = myClient.newOrderer(ordererConfig.url, {
    pem: ordererConfig.pem,
    'ssl-target-name-override': ordererConfig.hostname
  });

  let peerConfig = {
    hostname: 'ax-peer',
    url: 'grpc://localhost:7051', // change to grpcs://ax-peer:7051 in some condition (no idea?)
    eventHubUrl: 'grpc://localhost:7053',
    pem: readCryptoFile('axOrg.pem')
  };
  const defaultPeer = myClient.newPeer(peerConfig.url, {
    pem: peerConfig.pem,
    'ssl-target-name-override': peerConfig.hostname
  });
  // console.log(defaultPeer);
  myClient.setStateStore(await client.newDefaultKeyValueStore({
    path: './ax-peer'
  }))

let url = 'http://localhost:7054'
    const ca = new CAClient(url, {
      verify: false
    });
    let enrollmentID = 'admin';
    let enrollmentSecret = 'adminpw';
    const enrollment = await ca.enroll({
      enrollmentID: 'admin',
      enrollmentSecret: 'adminpw'
    });
    user = new User(enrollmentID, myClient);
    // console.log(enrollment);
    await user.setEnrollment(enrollment.key, enrollment.certificate, 'AxOrgMSP');

The above will check if admin user is available in the state store. Some queries regarding the above process

  1. The admin user generated here can be used to interact with any and all peers of the same org, assuming only one CA is used?
  2. What is a practical use of this identity, since for the rest of the functions, the admin identity generated by cryptogen for each peer is used (code below)
  3. While enrolling the admin, no attrs are passed along in ca.enroll(), so naturally when querying the identity roles field returns null. The ca-server link shared clearly assigns roles of client, user, peer, validator, auditor to it. Shouldn't that reflect here since it uses admin and adminpw for enrolling id and secret?

Continuing the code

  // crypto material got from cryptogen and shifted to new folder
  let adminUser = await myClient.createUser({
    username: `Admin@ax-peer`,
    mspid: 'AxOrgMSP',
    cryptoContent: {
      privateKeyPEM: readCryptoFile('[email protected]'),
      signedCertPEM: readCryptoFile('[email protected]')
    }
  });
  let txId = myClient.newTransactionID();
  let envelope_bytes = fs.readFileSync('./channel.tx');
  let channelConfig = myClient.extractChannelConfig(envelope_bytes);
  let signature = myClient.signChannelConfig(channelConfig);
  const request = {
      name: 'default',
      orderer: orderer,
      config: channelConfig,
      signatures: [signature],
      txId: txId
    };
   const response = await myClient.createChannel(request); // should be 200
   // rest of code joins channel, installs and instantiates chaincode
   // docker logs show init function being called in new cc container

The stateStore has two files in it (as expected), called admin and Admin@ax-peer. These look like

"name": "Admin@ax-peer",
  "mspid": "AxOrgMSP",
  "roles": null,
  "affiliation": "",
  "enrollmentSecret": "",
  enrollment: {
    "signingIdentity": "554a5f5cfc5a59231a04b7b051bcbcb4f79c4226ff336a4aa48b551de4a8428f",
    "certificate": "-----BEGIN CERTIFICATE----- xyz -----END CERTIFICATE-----"
  }

When this user is used from the state store by await myClient.getUserContext('admin', true);, how does the client sign the transactions? I am unable to locate the private key/ cert for any user created using the fabric-client SDK.

Now If I use fabric-network API, a FileSystemWallet() function is implemented that stores the private and public cert for each user made by it.

const enrollment = await ca.enroll({ enrollmentID: `admin`, enrollmentSecret: `adminpw` });
const identity = X509WalletMixin.createIdentity('AxOrgMSP', enrollment.certificate, enrollment.key.toBytes());
wallet.import('admin', identity);

This function serves the same purpose as the ca.enroll() but stores the private cert in a visible manner. If I want to use the users created by fabric-client in the fabric-network SDK, I would need to shift the certificates, how do I achieve this?

TLDR - Register user using fabric-client and then use Gateway() class from fabric-network to submit transactions with the same user.

Any advice, guidance is much appreciated. Thanks!

Upvotes: 2

Views: 2102

Answers (1)

david_k
david_k

Reputation: 5868

There are too many questions in this stack overflow, so all I will give is some insight into what you have posted.

  1. The admin identity you enroll from the ca server is called admin because it is a registrar of the fabric ca server and thus can register more identities in the ca server. It is not an admin identity for anything else such as the fabric network.
  2. You have used both the lower level api and the fabric-network mechanism for identity management. In the lower level api all that got persisted to the file system was the state store, the public cert and private key are stored in memory purely because of the way you have called everything it just happens to setup defaults in that manner, unfortunately other approaches using the low level api may require you to explicitly set the cryptosuite with a cryptoKeyStore. In the wallet implementation you probably used the file system wallet, so everything got persisted to the file system.

My recommendation is to start everything with a fabric-network gateway if you can. Then if you need to drop to the lower level api's because the fabric-network doesn't do what you need then you can call

gateway.getClient()

or

network.getChannel()

to get a client/channel that has been pre-configured with the gateway configuration and identity. To get access to a Certificate Authority you can use gateway.getClient().getCertificateAuthority() All of this allows you to use the wallet implementation for identity management (and the wallet implementation provides different persistence mechanisms such as in memory, file system, couchdb or you can write your own)

Upvotes: 4

Related Questions