diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index bdcf5bf843..7e511c5e3f 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -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}. @@ -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)); } @@ -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'); diff --git a/fabric-client/lib/api.js b/fabric-client/lib/api.js index e41eb61609..6be8bf1365 100755 --- a/fabric-client/lib/api.js +++ b/fabric-client/lib/api.js @@ -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 opts. * This operation is needed for deriving private keys corresponding to the Transaction Certificates. diff --git a/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js b/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js index 43ee675d9b..a396c63577 100755 --- a/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js +++ b/fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js @@ -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); diff --git a/fabric-client/lib/impl/ecdsa/pkcs11_key.js b/fabric-client/lib/impl/ecdsa/pkcs11_key.js index 04ae2d5b08..4a77b5d061 100644 --- a/fabric-client/lib/impl/ecdsa/pkcs11_key.js +++ b/fabric-client/lib/impl/ecdsa/pkcs11_key.js @@ -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()){ diff --git a/test/unit/client.js b/test/unit/client.js index 6e491f8fc7..ef05b721c1 100644 --- a/test/unit/client.js +++ b/test/unit/client.js @@ -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'); diff --git a/test/unit/cryptosuite-ecdsa-aes.js b/test/unit/cryptosuite-ecdsa-aes.js index 905dd530d2..eb327c15ab 100644 --- a/test/unit/cryptosuite-ecdsa-aes.js +++ b/test/unit/cryptosuite-ecdsa-aes.js @@ -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(); diff --git a/test/unit/network-config.js b/test/unit/network-config.js index 33605925d0..1584d31910 100644 --- a/test/unit/network-config.js +++ b/test/unit/network-config.js @@ -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'); @@ -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'); @@ -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'); @@ -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'); @@ -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(); @@ -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'); @@ -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'); @@ -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'); @@ -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'); @@ -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'); @@ -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); @@ -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'); @@ -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); @@ -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']); }, @@ -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'); }, @@ -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'); @@ -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 : { @@ -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');