How to get file-system wallet to use with the fabric-gateway-java sample

707 views Asked by At

I have a blockchain deployed on IBM cloud. I implemented a Node.js client app that enrolls the Admin user for MyOrg and imports a wallet locally on the file system. Once the wallet is imported I can submit transactions on the chaincode to upload data in the ledger and evaluate transactions to query the ledger. Everything is working with the Node.js client.

I now want to implement a similar Hyperledger client in Java. The fabric-gateway sample at https://hyperledger.github.io/fabric-gateway-java/ is very similar to the Node.js code for uploading and querying the ledger. Comparing the basic Node.js code below with the Java gateway sample code, there is a clear correspondence between the Node.js and Java classes used (both have classes with similar methods for Gateway, Network, Contract):

// Node.js code
// Create a new gateway, and connect to the gateway peer node(s). 
gateway = new Gateway();
await gateway.connect(ccp, { wallet: wallet, identity: OrgAdmin, discovery: {"enabled": true, "asLocalhost":false }});

// Get the network channel that the smart contract is deployed to.
let network = await gateway.getNetwork('mychannel');

// Get the smart contract from the network channel.
let contract = network.getContract('mycontract');

// submit transaction to add data in the ledger
await contract.submitTransaction('txName', txParam1, txParam2);

// query the ledger
let queryResult = await contract.evaluateTransaction('queryTx', queryStr);

The problem is that the Java gateway sample assumes there is already a wallet available locally and does not show how to create the wallet. I tried using the wallet directory imported by the Node.js client with the Java gateway sample but it does not work. It throws an exception when calling the .identity() method below (saying the identity specified "MyOrg_Admin" does not exist):

// Configure the gateway connection used to access the network. Gateway.Builder builder = Gateway.createBuilder() .identity(wallet, "MyOrg_Admin") .networkConfig(networkConfigFile);

I tried using the lower-level fabric-sdk-java classes to create the wallet. Below is the Node.js code to enroll the Admin user for MyOrg and import the wallet locally:

async function enrollOrgAdmin(aOrgName, aOrgCAUrl, aWalletFilePath)
{
  try
  {
    // Create a new CA client for interacting with the CA.
    const ca = new FabricCAServices(aOrgCAUrl);

    // Create a new file system based wallet for managing identities.
    const wallet = new FileSystemWallet(aWalletFilePath);

    // Enroll the admin user, and import the new identity into the wallet.
    const enrollment = await ca.enroll({ enrollmentID: OrgAdmin, enrollmentSecret: OrgAdminPasswd });
    const identity = X509WalletMixin.createIdentity(aOrgName, enrollment.certificate, enrollment.key.toBytes());
    await wallet.import(OrgAdmin, identity);
  }
  catch(error)
  {
    console.error(`Failed to enroll "${OrgAdmin}": ${error}`);
    process.exit(1);
  }
}

The only information needed is the OrgCA URL and organization name (both available from parsing the connection.json file) and the organization Admin username and password (which you just have to know). It seems that the corresponding Java class for FabricCAServices is HFCAClient. Googling for code samples using this class the most useful link is the one below (besides the unit tests of the fabric-sdk-java source code): https://developer.ibm.com/technologies/blockchain/tutorials/hyperledger-fabric-java-sdk-for-tls-enabled-fabric-network/

My Java code to enroll the Admin user (the 1st step in importing the wallet):

String pemStr = "copy-pasted from connection.json['certificateAuthorities'][adr]['tlsCACerts']['pem']";
Properties caProperties = new Properties();
caProperties.put("pemBytes", pemStr.getBytes());
    
try
{
    // caName and caUrl copy-pasted from connection.json['certificateAuthorities'][adr]['caName'] and ['url']
    HFCAClient ca = HFCAClient.createNewInstance(caName, caUrl, caProperties);
    CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
    ca.setCryptoSuite(cryptoSuite);
    Enrollment adminEnrollment = ca.enroll("MyOrg_Admin", "password");
}
catch (Exception e) {
    e.printStackTrace();
}

The ca.enroll() call throws this exception: ERROR org.hyperledger.fabric_ca.sdk.HFCAClient - org.hyperledger.fabric.sdk.exception.CryptoException: Unable to add CA certificate to trust store. Error: java.io.IOException: Incomplete data

Can you help to enroll the Admin user? What am I doing wrong?

Thanks, Ionut

1

There are 1 answers

0
user2960174 On

The problem was from Eclipse IDE: when copy pasting the PEM certificate from connection.json["certificateAuthorities"][adr]["tlsCACerts"]["pem"] Eclipse escaped the newline char, so "\n" became "\\n". So to summarise the Java enrolment works with the PEM certificate taken from connection.json["certificateAuthorities"][adr]["tlsCACerts"]["pem"]; The sample file EnrollAdmin.java ( https://github.com/hyperledger/fabric-samples/blob/1cd71fd26a867efcb0577e02fa42a48a3203ec51/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java ) shows how to import the wallet once the Admin user is successfully enrolled.