2

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('Admin@ax-org-key.pem'),
      signedCertPEM: readCryptoFile('Admin@ax-org-cert.pem')
    }
  });
  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!

Varun Agarwal
  • 1,587
  • 14
  • 29

1 Answers1

4

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)

david_k
  • 5,843
  • 2
  • 9
  • 16
  • Thank you. I will try this. Though I found a hidden folder called `.hfc-key-store` in the home directory (Ubuntu) that is storing the private key corresponding to users enrolled in low level API. I went hunting through the `fabric-client` and `fabric-ca-client` repo and found `hfc-key-store` to be the default storage location. But this becomes a bit messy since the public cert and priv key are stored in two separate folders. Yeah I got alot of questions, just finished a PoC and moving on to a large scale pilot so got to handle lot of additional things. – Varun Agarwal Jun 14 '19 at 10:24
  • Also I noticed the keys generated keep changing, it is not deterministic based on enrollment ID and secret, unlike public blockchain implementations. – Varun Agarwal Jun 14 '19 at 10:26
  • Every time you enroll, you create a new private key and a new certificate is generated from that private key. Therefore every time you enroll you get a different identity which just happens to have the same Common Name. Also note that private keys are only stored in .hfc-key-store if you use a non ephemeral cryptoKeyStore – david_k Jun 14 '19 at 11:30
  • Ya I make sure to put `true` in persistence option. Though interestingly all identity functions from gateway are passed to the client instance, but when enrolling a user from gateway, the certificate, pub an prvt key are saved, whereas when enrolling from client only private key and certificate are saved ! – Varun Agarwal Jun 14 '19 at 13:20