diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index bd4ff10da1..3645a50a78 100755 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -837,7 +837,7 @@ const Channel = class { const authentication = new _discoveryProto.AuthInfo(); authentication.setClientIdentity(signer.serialize()); - const cert_hash = this._clientContext.getClientCertHash(); + const cert_hash = this._clientContext.getClientCertHash(true); if (cert_hash) { authentication.setClientTlsCertHash(cert_hash); } diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index b39bfe3fa3..b0e07150ff 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -95,6 +95,7 @@ const Client = class extends BaseClient { // When using a network configuration (connection profile) the client // side mutual tls cert and key must be stored here + // -- also store the hash after computing this._tls_mutual = {}; this._organizations = new Map(); @@ -150,32 +151,31 @@ const Client = class extends BaseClient { logger.debug('setTlsClientCertAndKey - start'); this._tls_mutual.clientCert = clientCert; this._tls_mutual.clientKey = clientKey; + this._tls_mutual.clientCertHash = null; } /** * 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. + * If the tls client material has not been set for the client, it will be + * generated if the user and crypto suite has been assigned to this client. * @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 || !this._tls_mutual.clientKey) { + if (this._cryptoSuite && this._userContext) { + logger.debug('addTlsClientCertAndKey - generating self-signed TLS client certificate'); + // generate X509 cert pair + let key = this._cryptoSuite.generateEphemeralKey(); + this._tls_mutual.clientKey = key.toBytes(); + this._tls_mutual.clientCert = key.generateX509Certificate(this._userContext.getName()); + } + } // use client cert pair if it exists if (this._tls_mutual.clientCert && this._tls_mutual.clientKey) { opts.clientCert = this._tls_mutual.clientCert; 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()); } } @@ -1776,22 +1776,30 @@ const Client = class extends BaseClient { /** * Get the client certificate hash + * @param {boolean} create - Optional. Create the hash based on the current + * user if the cleint cert has not been assigned to this client * @returns {byte[]} The hash of the client certificate */ - getClientCertHash() { + getClientCertHash(create) { const method = 'getClientCertHash'; logger.debug('%s - start', method); + if(this._tls_mutual.clientCertHash) { + return this._tls_mutual.clientCertHash; + } + if(!this._tls_mutual.clientCert && create) { + // this will create the cert and key from the current user if available + this.addTlsClientCertAndKey({}); + } - let hash = null; if (this._tls_mutual.clientCert) { logger.debug('%s - using clientCert %s', method, this._tls_mutual.clientCert); let der_cert = sdkUtils.pemToDER(this._tls_mutual.clientCert); - hash = computeHash(der_cert); + this._tls_mutual.clientCertHash = computeHash(der_cert); } else { logger.debug('%s - no tls client cert', method); } - return hash; + return this._tls_mutual.clientCertHash; } _checkTLScert_n_key(opts) { diff --git a/test/integration/e2e/e2eUtils.js b/test/integration/e2e/e2eUtils.js index dcdefe9484..0a8dcc3193 100644 --- a/test/integration/e2e/e2eUtils.js +++ b/test/integration/e2e/e2eUtils.js @@ -278,7 +278,7 @@ function instantiateChaincodeWithId(userOrg, chaincode_id, chaincode_path, versi // this is the longest response delay in the test, sometimes // x86 CI times out. set the per-request timeout to a super-long value - return channel.sendUpgradeProposal(request, 5*60*1000) + return channel.sendUpgradeProposal(request, 10*60*1000) .then((results) => { let proposalResponses = results[0]; @@ -303,7 +303,7 @@ function instantiateChaincodeWithId(userOrg, chaincode_id, chaincode_path, versi version, language, upgrade, transientMap); tx_id = request.txId; - return channel.sendUpgradeProposal(request, 5*60*1000); + return channel.sendUpgradeProposal(request, 10*60*1000); } else { throw new Error('Failed to test for bad transient map. The chaincode should have rejected the upgrade proposal.'); } @@ -318,9 +318,9 @@ function instantiateChaincodeWithId(userOrg, chaincode_id, chaincode_path, versi // this is the longest response delay in the test, sometimes // x86 CI times out. set the per-request timeout to a super-long value if(upgrade) { - return channel.sendUpgradeProposal(request, 5*60*1000); + return channel.sendUpgradeProposal(request, 10*60*1000); } else { - return channel.sendInstantiateProposal(request, 5*60*1000); + return channel.sendInstantiateProposal(request, 10*60*1000); } } diff --git a/test/unit/client.js b/test/unit/client.js index 5fb6736610..63e07b2957 100644 --- a/test/unit/client.js +++ b/test/unit/client.js @@ -1033,27 +1033,36 @@ test('\n\n*** Test normalizeX509 ***\n', function(t) { test('\n\n*** Test Add TLS ClientCert ***\n', function (t) { var testClient = new Client(); - t.throws( + t.doesNotThrow( () => { testClient.addTlsClientCertAndKey({}); }, /A crypto suite has not been assigned to this client/, - 'Check that error is thrown when crypto suite is not set' + 'Check that error is not thrown when crypto suite is not set' ); testClient.setCryptoSuite(Client.newCryptoSuite()); - t.throws( + t.doesNotThrow( () => { testClient.addTlsClientCertAndKey({}); }, /A user context has not been assigned to this client/, - 'Check that error is thrown when user context is not set' + 'Check that error is not thrown when user context is not set' ); testClient.setUserContext(new User('testUser'), true); try { + t.notOk(testClient._tls_mutual.clientKey, 'Check that client key is not there'); + t.notOk(testClient._tls_mutual.clientCert, 'Check that client certain is not there'); + t.notOk(testClient._tls_mutual.clientCertHash, 'Check that cert hash was not cached'); + + t.ok(testClient.getClientCertHash(true), 'Check forcing the hash to be based off the user'); + t.ok(testClient._tls_mutual.clientCertHash, 'Check that cert hash was cached'); + 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'); + t.ok(testClient._tls_mutual.clientKey, 'Check that client key is there'); + t.ok(testClient._tls_mutual.clientCert, 'Check that client cert is there'); } catch (err) { t.fail('addTlsClientCertandKey failed: ' + err); } @@ -1063,6 +1072,7 @@ test('\n\n*** Test Add TLS ClientCert ***\n', function (t) { test('\n\n*** Test Set and Add TLS ClientCert ***\n', function(t) { let client = new Client(); + t.notOk(client.getClientCertHash(), 'Check getting null hash when no client cert assigned'); client.setTlsClientCertAndKey(aPem, aPem); t.pass('Able to set the client cert and client key'); const tls_cert_key = {};