How to share user certificates to clients - hyperledger-fabric

Which is the most appropriate way to share client certificates to end users of a Hyperledger fabric network?
I have already set up a java sdk client to Register and enroll users using admin Credentials. At the end of the scenario i have a Username and a Password for each user. What i cannot find is where client certificates are stored at the local MSP and how i can share them to the actual end users.
Any recommendation or example of a proposed solution will be appreciated.

while enrolling user,
you will get enrollment object
Irrespective of SDK (NODE, JAVA, GO)
let enrollment = await caClient.enroll(request)
const key = enrollment.key.toBytes();
const cert = enrollment.certificate;
response.key = key;
response.cert = cert;
response.secret = secret;
return response;
LOOKS like below result
"data": { "message": "nbdClient has been enrolled Successfully to
Org: nbd", "key": "-----BEGIN PRIVATE
KEY-----\r\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgH7ttkV4VIDA1TlSx\r\n/bmsy1Ad6zgLGhjdcFtElexqAtShRANCAASHKIOk+nBTIqfn5taiqMWlRnfHKdth\r\nkZKyq9Up4wl+PsBEQByyKfaDV904APCQ7zDvmPtwxsdNGxA76V4EpAqO\r\n-----END
PRIVATE KEY-----\r\n", "cert": "-----BEGIN
CERTIFICATE-----\nMIICjTCCAjOgAwIBAgIUA85ydnzJXRoRxeW5v2lrNk5pe+swCgYIKoZIzj0EAwIw\nWTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xDDAKBgNVBAoTA25iZDEPMA0GA1UEAxMGY2EubmJkMB4XDTE5\nMDgxNTE0MjQwMFoXDTIwMDgxNDE0MjkwMFowLzEZMAsGA1UECxMEdXNlcjAKBgNV\nBAsTA25iZDESMBAGA1UEAxMJbmJkQ2xpZW50MFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEhyiDpPpwUyKn5+bWoqjFpUZ3xynbYZGSsqvVKeMJfj7AREAcsin2g1fd\nOADwkO8w75j7cMbHTRsQO+leBKQKjqOCAQEwgf4wDgYDVR0PAQH/BAQDAgOoMB0G\nA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud\nDgQWBBRMV5nFrtT3IpichyfayTaACbqpQDArBgNVHSMEJDAigCBLcH2ot2qhX7wR\nCP6IeDXWkgXitZ3TukhQLBZFzboaWzBzBggqAwQFBgcIAQRneyJhdHRycyI6eyJo\nZi5BZmZpbGlhdGlvbiI6Im5iZCIsImhmLkVucm9sbG1lbnRJRCI6Im5iZENsaWVu\ndCIsImhmLlR5cGUiOiJ1c2VyIiwibmJkVXNlcjEiOiJuYmRVc2VyMSJ9fTAKBggq\nhkjOPQQDAgNIADBFAiEA9/Rqd9/WtWLkR+XE1MdS4gX/JdYTqU58E8KMaShwFmkC\nIDkA2OsC0jRswweTHmzGk5z5gKcwhOrZbJTZagqpv2m4\n-----END
CERTIFICATE-----\n", "secret": "HqknKlBGzibb" }

Related

Hyperledger Fabric get User Private Key from Node.js SDK 2.2 version

I am building a Node.js application that uses the Hyperledger Fabric SDK 2.2 version that interacts with the blockchain network.
What I want to achieve, is to retrieve the Private Key of the user making the request from the Node.js application after retrieved the identity of the wallet.
// Create a new file system based wallet for managing identities.
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = await Wallets.newFileSystemWallet(walletPath);
logger.info(`API/` + requestType + `: Wallet path: ${walletPath}`);
// Check to see if we've already enrolled the user.
const userExists = await wallet.get(in_username);
if (!userExists) {
logger.info('API/` + requestType + `: An identity for the user: '+in_username+' does not exist in the wallet');
//return 'An identity for the user: "'+in_username+'" does not exist in the wallet';
throw new Error('An identity for the user: "'+in_username+'" does not exist in the wallet');
}
if (userExists && userExists.type === 'X.509') {
console.log("userPrivateKey 1 : " + userExists.type);
const privateKey = (userExists as X509Identity).credentials.privateKey;
console.log("userPrivateKey 1 : " + privateKey);
}
So, I have seen from the documentation the above example of retrieving the private key. I retrieve successfully the identity and get the type of the certificate which is truly X509.
But after that, I am unable to cast or convert the identity to X509 Identity in order to retrieve the private key.
At first I am not able to proceed as this error comes up in the row:
const privateKey = (userExists as X509Identity).credentials.privateKey;
Error:
Type assertion expressions can only be used in TypeScript files.
I am not expert in Node.js and I have been informed that this might not be possible to "cast". But I am confused since I have seen that in the documentation of the Hyperledger Fabric Node SDK.
Anyone knows any solution on that, or even a hint on how to continue?
Thank you
I was going too complex!
Since interface of X509 is extended from the Identity interface then the following code is working:
const privateKey = userExists.credentials.privateKey;
Easier than it seems and also tricky. Thanks for your time!

How to manage user authentication for hyperledger using msp and fabric-ca-client?

I am developing an application over fabric 1.3 . I have built a network on multi-node setup, connected peers, instantiate chaincode and have my network up and ready for invocation and queries.
Now, I am thinking to make a log-in portal through which a user can register/enroll and perform invoke/queries. All my peers and orderer are on cloud, and am planning to provide this log-in feature using the Node SDK exposed on a cloud instance.
I went through the official doc:
https://hyperledger-fabric-ca.readthedocs.io/en/latest/users-guide.html#registering-a-new-identity
I can see that we need fabric-ca component to register users and enroll them for queries. Upon enrollment, we get a cert files under ~/.hfc-key-store.
Now I want to understand how should I go ahead with my flow.
User signs up on network:
fabric_ca_client.register({enrollmentID: 'user1', affiliation: 'org1.department1'}, admin_user)
User log in with his secret:
fabric_ca_client.enroll({enrollmentID: 'user1', enrollmentSecret: secret});
}).then((enrollment) => {
console.log('Successfully enrolled member user "user1" ');
return fabric_client.createUser(
{username: 'user1',
mspid: 'Org1MSP',
cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
});
}).then((user) => {
member_user = user;
return fabric_client.setUserContext(member_user);
Invoke/Query as user1:
var store_path = path.join(os.homedir(), '.hfc-key-store');
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
// assign the store to the fabric client
fabric_client.setStateStore(state_store);
var crypto_suite = Fabric_Client.newCryptoSuite();
// use the same location for the state store (where the users' certificate are kept)
// and the crypto store (where the users' keys are kept)
var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
crypto_suite.setCryptoKeyStore(crypto_store);
fabric_client.setCryptoSuite(crypto_suite);
// get the enrolled user from persistence, this user will sign all requests
return fabric_client.getUserContext('user1', true);
}).then((user_from_store) => {
if (user_from_store && user_from_store.isEnrolled()) {
console.log('Successfully loaded user1 from persistence');
member_user = user_from_store;
} else {
throw new Error('Failed to get user1.... run registerUser.js');
}..
Now, what shall I do when a user logs out? delete the ~/.hfc-key-store certs? Since these certs are going to be stored on server side where Node script is running, so it doesn't make sense.
Also, is my flow correct or if there is any better way to accomplice my objective?
I had a similar login implementation that I had to do, As you said I did create certificates for each registered user as well as stored the basic user information in MongoDB.
The flow that I went with is that once the user is registered the user certification is created as well as his login credentials such as username and password are stored in MongoDB.
When the user tries to login back, I would check the MongoDB as well as the certification to see if the user has already registered, once logged in the user would then be in possession of an auth token which he then can use to interact with the fabric-client.
When an identity (certs and keys) is issued by CA then it should be persisted for that particular user for future interactions (either by saving in client wallet or other methods) so if you delete it then there will be re-enroll process on each log in and it will slow it down too.
To resolve it -
1. Create separate logic like JWT token for log in and session management.
2. Save the keys and certs on server directory (not the best way but will work for now later)
Let me know if it satisfies your query.
Maybe a little late but maybe it can help someone. My approach is to do what you do in the login, in a register method where I return the certificate and the private key, then on login the user needs to provide both the certificate and the private keys generated (and also the certificate and the private key for the TLS connection), and with this information I recreate de Identity and store it in a MemoryWallet, and with this MemoryWallet now, I can create the gateway and connect to the blockchain.
It would be something like this
const identity = X509WalletMixin.createIdentity(mspId, certificate, privateKey);
const identityTLS = X509WalletMixin.createIdentity(mspId, certificateTLS, privateKeyTLS);
const wallet = new InMemoryWallet();
await wallet.import(userId, identity);
await wallet.import(userId + '-tls', identityTLS);
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: userId, discovery: { enabled: true, asLocalhost: false } });
const client = gateway.getClient();
const userTlsCert = await wallet.export(userId + '-tls') as any;
client.setTlsClientCertAndKey(userTlsCert.certificate, userTlsCert.privateKey);
Hope it helps

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
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?
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)
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!
There are too many questions in this stack overflow, so all I will give is some insight into what you have posted.
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.
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)

Using a Microsoft Windows Certificate Store in node.js

I am trying to create a node https server. The idea is that the server will receive the thumbprint of a certificate in the store, pull it out of the store and use it for SSL/TLS connection. I have done examples for self-signed certificates and it has worked fine.
I have the code to look through the certificate store and find the correct certificate based on its thumbprint
The code I have so far is below.
const ca = require('win-ca');
const forge = require('node-forge');
const pki = forge.pki;
const ssh = forge.ssh;
const thumbprint = process.env.THUMBPRINT;
...
for (let cert of ca.all()) {
console.log(forge.pki.certificateToPem(cert));
console.log(' ----------- \r');
const md = forge.md.sha1.create();
md.update(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes());
const hex = md.digest().toHex();
console.log(hex);
console.log('\r');
if (thumbprint === hex) {
foundCert = cert;
console.log('found\r');
break;
}
}
console.log('**********************');
console.log('\r');
//}
cnt++;
}
if (foundCert) {
console.log(forge.pki.certificateToPem(foundCert));
const pm = forge.pki.certificateToPem(foundCert);
console.log(foundCert);
}
In windows ASP.NET Core, one only needs the the certificate for https. For node.js, all the examples use a private key. I don't have the private key, but I have access to the machine's certificate store. Can I use the certificate from the store without the private key? There seems to be a piece I am missing.
Thanks

Verify JWT Claims in WebAPI

Given the Thinktecture AuthenticationConfiguration below:
var authConfig = new AuthenticationConfiguration
{
EnableSessionToken = true,
SendWwwAuthenticateResponseHeaders = true,
RequireSsl = false,
ClaimsAuthenticationManager = new ClaimsTransformation(),
SessionToken = new SessionTokenConfiguration
{
EndpointAddress = "/api/token",
SigningKey = CryptoRandom.CreateRandomKey(32),
DefaultTokenLifetime = new TimeSpan(1, 0, 0)
}
};
It would return an example JWT of eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzZXNzaW9uIGlzc3VlciIsImF1ZCI6Imh0dHA6Ly9zZXNzaW9uLnR0IiwibmJmIjoxNDIwMzk2ODgyLCJleHAiOjE0MjA0MDA0ODIsInVuaXF1ZV9uYW1lIjoicGFzcyIsImF1dGhtZXRob2QiOiJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvYXV0aGVudGljYXRpb25tZXRob2QvcGFzc3dvcmQiLCJhdXRoX3RpbWUiOiIyMDE1LTAxLTA0VDE4OjQxOjA0LjAxOVoiLCJyb2xlIjoiVmVyaWZpZWQifQ.h7curaLrqkMT4Btg-AAoEpNYqUIYNQA_y-eUdEwQBqs
Which is:
{
"alg": "HS256",
"typ": "JWT"
}
{
"unique_name": "pass",
"aud": "http://session.tt",
"iss": "session issuer",
"authmethod": "http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password",
"role": "Verified",
"exp": 1420400482,
"auth_time": "2015-01-04T18:41:04.019Z",
"nbf": 1420396882
}
How would I verify that the JWT was issued from a trusted machine, can we use a symmetric key for the private signing key and the same key on the remote machine to verify against?
How could I wire up the WebAPI so that it automatically does this for us (assuming the AuthenticationConfiguration is on a different machine dedicated to account security api).
You can use a shared symmetric key or a private key to sign the JWT and that use that same symmetric key or respectively the associated public key to verify it.
The algorithm in use for this JWT (HS256) suggests that a shared symmetric key was used so you need to know that symmetric key at the receiving end in order to verify the JWT.

Resources