Skip to content

Commit

Permalink
[FAB-10682] Use self-signed TLS certs
Browse files Browse the repository at this point in the history
Service Discovery requires TLS client auth.
In the case where mutual TLS is not required by
the remote node, the client will generate a
self-signed TLS cert and use it for TLS
communications.

Change-Id: I5908f6d17ffdfa6371c113a836489c80c84f32c7
Signed-off-by: Gari Singh <[email protected]>
  • Loading branch information
mastersingh24 committed Jun 22, 2018
1 parent 29fbf11 commit 23ec52f
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 13 deletions.
36 changes: 24 additions & 12 deletions fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,32 @@ const Client = class extends BaseClient {
}

/**
* Utility method to add the mutual tls client material to a set of options
* Utility method to add the mutual tls client material to a set of options.
* If the tls client material has not been set for the client, it will be generated.
* @param {object} opts - The options object holding the connection settings
* that will be updated with the mutual TLS clientCert and clientKey.
* @throws Will throw an error if generating the tls client material fails
*/
addTlsClientCertAndKey(opts) {
if (this._tls_mutual.clientCert) {
// use client cert pair if it exists
if (this._tls_mutual.clientCert && this._tls_mutual.clientKey) {
opts.clientCert = this._tls_mutual.clientCert;
}
if (this._tls_mutual.clientKey) {
opts.clientKey = this._tls_mutual.clientKey;
} else {
if (!this._cryptoSuite) {
throw new Error('A crypto suite has not been assigned to this client');
}
if (!this._userContext) {
throw new Error('A user context has not been assigned to this client');
}
logger.debug('addTlsClientCertAndKey - generating self-signed TLS client certificate');
// generate X509 cert pair
let key = this._cryptoSuite.generateEphemeralKey();
opts.clientKey = key.toBytes();
opts.clientCert = key.generateX509Certificate(this._userContext.getName());
}
}


/**
* Determine if the fabric backend is started in
* [development mode]{@link http://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html?highlight=develop%20mode#testing-using-dev-mode}.
Expand Down Expand Up @@ -1203,8 +1215,8 @@ const Client = class extends BaseClient {
if (!crypto_suite) {
crypto_suite = BaseClient.newCryptoSuite();
}
const key = crypto_suite.importKey(private_key, {ephemeral: true});
const public_key = crypto_suite.importKey(certificate, {ephemeral: true});
const key = crypto_suite.importKey(private_key, { ephemeral: true });
const public_key = crypto_suite.importKey(certificate, { ephemeral: true });

this._adminSigningIdentity = new SigningIdentity(certificate, public_key, mspid, crypto_suite, new Signer(crypto_suite, key));
}
Expand Down Expand Up @@ -1643,19 +1655,19 @@ const Client = class extends BaseClient {
// first load the private key and save in the BCCSP's key store

let importedKey;
const user = new User(opts.username) ;
const user = new User(opts.username);
let privateKeyPEM = opts.cryptoContent.privateKeyPEM;
if (opts.cryptoContent.privateKey) {
privateKeyPEM = await readFile(opts.cryptoContent.privateKey);
}
if(privateKeyPEM){
if (privateKeyPEM) {
logger.debug('then privateKeyPEM data');
importedKey = await this.getCryptoSuite().importKey(privateKeyPEM.toString(), {ephemeral: !this.getCryptoSuite()._cryptoKeyStore});
}else {
importedKey = await this.getCryptoSuite().importKey(privateKeyPEM.toString(), { ephemeral: !this.getCryptoSuite()._cryptoKeyStore });
} else {
importedKey = opts.cryptoContent.privateKeyObj;
}
let signedCertPEM = opts.cryptoContent.signedCertPEM;
if(opts.cryptoContent.signedCert){
if (opts.cryptoContent.signedCert) {
signedCertPEM = await readFile(opts.cryptoContent.signedCert);
}
logger.debug('then signedCertPEM data');
Expand Down
9 changes: 9 additions & 0 deletions fabric-client/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,18 @@ module.exports.CryptoSuite = class {
*
* @param {KeyOpts} opts Optional
* @returns {module:api.Key} Promise for an instance of the Key class
* @throws Will throw an error if not implemented
*/
generateKey(opts) {if(opts);}

/**
* Generate an ephemeral key.
*
* @returns {module:api.Key} An instance of the Key class
* @throws Will throw an error if not implemented
*/
generateEphemeralKey() {}

/**
* Derives the new private key from the source public key using the parameters passed in the <code>opts</code>.
* This operation is needed for deriving private keys corresponding to the Transaction Certificates.
Expand Down
5 changes: 5 additions & 0 deletions fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ class CryptoSuite_ECDSA_AES extends api.CryptoSuite {
this._cryptoKeyStore = cryptoKeyStore;
}

generateEphemeralKey() {
const pair = KEYUTIL.generateKeypair('EC', this._curveName);
return new ECDSAKey(pair.prvKeyObj);
}

async generateKey(opts) {
const pair = KEYUTIL.generateKeypair('EC', this._curveName);

Expand Down
8 changes: 8 additions & 0 deletions fabric-client/lib/impl/ecdsa/pkcs11_key.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ var PKCS11_ECDSA_KEY = class extends api.Key {

}

generateKey(opts) {
throw new Error('Not implemented');
}

generateEphemeralKey() {
throw new Error('Not implemented');
}

generateCSR(subjectDN) {
//check to see if this is a private key
if (!this.isPrivate()){
Expand Down
32 changes: 31 additions & 1 deletion test/unit/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,37 @@ test('\n\n*** Test normalizeX509 ***\n', function(t) {
t.end();
});

test('\n\n*** Test TLS ClientCert ***\n', function(t) {
test('\n\n*** Test Add TLS ClientCert ***\n', function (t) {
var testClient = new Client();
t.throws(
() => {
testClient.addTlsClientCertAndKey({});
},
/A crypto suite has not been assigned to this client/,
'Check that error is thrown when crypto suite is not set'
);
testClient.setCryptoSuite(Client.newCryptoSuite());
t.throws(
() => {
testClient.addTlsClientCertAndKey({});
},
/A user context has not been assigned to this client/,
'Check that error is thrown when user context is not set'
);
testClient.setUserContext(new User('testUser'), true);
try {
const tls_cert_key = {};
testClient.addTlsClientCertAndKey(tls_cert_key);
t.ok(tls_cert_key.clientCert, 'Check that clientCert exists');
t.ok(tls_cert_key.clientKey, 'Check that clientKey exists');
} catch (err) {
t.fail('addTlsClientCertandKey failed: ' + err);
}

t.end();
});

test('\n\n*** Test Set and Add TLS ClientCert ***\n', function(t) {
let client = new Client();
client.setTlsClientCertAndKey(aPem, aPem);
t.pass('Able to set the client cert and client key');
Expand Down
14 changes: 14 additions & 0 deletions test/unit/cryptosuite-ecdsa-aes.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,20 @@ test('\n\n ** CryptoSuite_ECDSA_AES - error tests **\n\n', (t) => {

});

test('\n\n ** CryptoSuite_ECDSA_AES - generateEphemeralKey tests **\n\n', function (t) {
testutil.resetDefaults();
var cryptoUtils = utils.newCryptoSuite();
var key = cryptoUtils.generateEphemeralKey();
if (key && key._key && key._key.type === 'EC') {
t.pass('generateEphemeralKey returned key');
t.end();
} else {
t.fail('generateEphemeralKey did not return key');
t.end();
}

});

test('\n\n ** CryptoSuite_ECDSA_AES - ephemeral true tests **\n\n', function (t) {
testutil.resetDefaults();
var cryptoUtils = utils.newCryptoSuite();
Expand Down
35 changes: 35 additions & 0 deletions test/unit/network-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const yaml = require('js-yaml');
const util = require('util');

const Client = require('fabric-client');
const User = require('fabric-client/lib/User.js');
const Peer = require('fabric-client/lib/Peer.js');
const Orderer = require('fabric-client/lib/Orderer.js');
const Organization = require('fabric-client/lib/Organization.js');
Expand Down Expand Up @@ -108,6 +109,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.equals('Org1', client._network_config._network_config.client.organization, ' org should be Org1');
client.loadFromConfig('test/fixtures/org2.yaml');
t.equals('Org2', client._network_config._network_config.client.organization, ' org should be Org2');
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
const channel = client.getChannel('mychannel2');
let event_hubs = client.getEventHubsForOrg();
t.equals('localhost:8053', event_hubs[0].getPeerAddr(), ' Check to see if we got the right event hub for org2 by default');
Expand All @@ -124,6 +127,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.equals('Org2MSP', client.getMspid(), ' check to see if we can get the mspid of the current clients organization');

const client2 = Client.loadFromConfig('test/fixtures/network2.yaml');
client2.setCryptoSuite(Client.newCryptoSuite());
client2.setUserContext(new User('testUser'), true);
client2.loadFromConfig('test/fixtures/org1.yaml');
t.equals(client2.getPeersForOrg().length, 3, ' Check to see that we got 3 peers for Org1');
const channel3 = client2.getChannel('mychannel3');
Expand Down Expand Up @@ -188,6 +193,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
const file_data = fs.readFileSync(config_loc);
const network_data = yaml.safeLoad(file_data);
const client = Client.loadFromConfig(network_data);
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client.loadFromConfig(network_data);
const channel = client.getChannel('mychannel2');
t.equals(channel.getPeers()[0].getUrl(),'grpcs://localhost:7051',' check to see that the peer has been added to the channel');
Expand All @@ -212,6 +219,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.throws(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig({}, client);
client.setCryptoSuite({cryptoSuite : 'cryptoSuite'});
client.getCertificateAuthority();
Expand All @@ -229,6 +238,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const channel = client.newChannel('mychannel');
t.equals('mychannel',channel.getName(),'Channel should be named');
Expand All @@ -255,6 +266,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const channel = client.getChannel('mychannel');
t.equals('mychannel',channel.getName(),'Channel should be named');
Expand Down Expand Up @@ -283,6 +296,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const channel = client.getChannel('mychannel');
t.equals('mychannel',channel.getName(),'Channel should be named');
Expand Down Expand Up @@ -311,6 +326,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const channel = client.getChannel('mychannel');
t.equals('mychannel',channel.getName(),'Channel should be named');
Expand Down Expand Up @@ -371,6 +388,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const channel = client.getChannel('mychannel');
t.equals('mychannel',channel.getName(),'Channel should be named');
Expand All @@ -388,6 +407,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);

let targets = client.getTargetPeers('peer1', client);
Expand Down Expand Up @@ -495,6 +516,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);
const organizations = client._network_config.getOrganizations();
if(Array.isArray(organizations)) t.pass('organizations is an array');
Expand Down Expand Up @@ -528,6 +551,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
let client = Client.loadFromConfig(network_config);
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
let channel = client.getChannel('mychannel');

checkTarget(channel._getTargetForQuery(), '7053', 'finding a default ledger query', t);
Expand Down Expand Up @@ -559,6 +584,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.throws(
() => {
const client = Client.loadFromConfig(network_config);
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
const channel = client.getChannel('mychannel');
channel._getTargetForQuery(['peer1']);
},
Expand All @@ -569,6 +596,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.throws(
() => {
const client = Client.loadFromConfig(network_config);
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
const channel = client.getChannel('mychannel');
channel._getTargets('bad');
},
Expand Down Expand Up @@ -609,6 +638,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client._network_config = new NetworkConfig(network_config, client);

let orderer = client.getTargetOrderer('orderer0');
Expand Down Expand Up @@ -636,6 +667,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {
t.doesNotThrow(
() => {
const client = Client.loadFromConfig(network_config);
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
let channel = client.getChannel('mychannel');
client.loadFromConfig({ version: '1.0.0',
channels : {
Expand Down Expand Up @@ -844,6 +877,8 @@ test('\n\n ** configuration testing **\n\n', function (t) {

test('\n\n ** channel testing **\n\n', function (t) {
const client = new Client();
client.setCryptoSuite(Client.newCryptoSuite());
client.setUserContext(new User('testUser'), true);
client.loadFromConfig('test/fixtures/network.yaml');

const channel = client.getChannel('mychannel2');
Expand Down

0 comments on commit 23ec52f

Please sign in to comment.