diff --git a/fabric-ca-client/lib/FabricCAClientImpl.js b/fabric-ca-client/lib/FabricCAClientImpl.js index a3c692c617..949d83d1a8 100644 --- a/fabric-ca-client/lib/FabricCAClientImpl.js +++ b/fabric-ca-client/lib/FabricCAClientImpl.js @@ -24,7 +24,9 @@ var path = require('path'); var http = require('http'); var https = require('https'); var urlParser = require('url'); -var x509 = require('x509'); +var jsrsasign = require('jsrsasign'); +var x509 = jsrsasign.X509; +var ASN1HEX = jsrsasign.ASN1HEX; var logger = utils.getLogger('FabricCAClientImpl.js'); @@ -205,19 +207,16 @@ var FabricCAServices = class extends BaseClient { } var cert = currentUser.getIdentity()._certificate; - var subject; + var subject = null; try { - subject = x509.getSubject(FabricCAServices.normalizeX509(cert)); + subject = getSubjectCommonName(FabricCAServices.normalizeX509(cert)); } catch(err) { logger.error(util.format('Failed to parse enrollment certificate %s for Subject. \nError: %s', cert, err)); } - if (subject === null || subject === {}) + if (subject === null) throw new Error('Failed to parse the enrollment certificate of the current user for its subject'); - if (!subject.commonName) - throw new Error('Invalid enrollment certificate of the current user: does not contain the "CN" value'); - var self = this; return new Promise(function (resolve, reject) { @@ -227,7 +226,7 @@ var FabricCAServices = class extends BaseClient { function (privateKey) { //generate CSR using the subject of the current user's certificate try { - var csr = privateKey.generateCSR('CN=' + subject.commonName); + var csr = privateKey.generateCSR('CN=' + subject); self._fabricCAClient.reenroll(csr, currentUser.getSigningIdentity()) .then( function (response) { @@ -783,5 +782,20 @@ function checkRegistrar(registrar) { } } +// This utility is based on jsrsasign.X509.getSubjectString() implementation +// we can not use that method directly because it requires calling readCertPEM() +// first which as of jsrsasign@6.2.3 always assumes RSA based certificates and +// fails to parse certs that includes ECDSA keys. +function getSubjectCommonName(pem) { + var hex = x509.pemToHex(pem); + var d = ASN1HEX.getDecendantHexTLVByNthList(hex, 0, [0, 5]); + var subject = x509.hex2dn(d); // format: '/C=US/ST=California/L=San Francisco/CN=Admin@org1.example.com/emailAddress=admin@org1.example.com' + var m = subject.match(/CN=.+[^\/]/); + if (!m) + throw new Error('Certificate PEM does not seem to contain a valid subject with common name "CN"'); + else + return m[0].substring(3); +} + module.exports = FabricCAServices; module.exports.FabricCAClient = FabricCAClient; diff --git a/fabric-ca-client/package.json b/fabric-ca-client/package.json index 56776cccc1..b4f0ffb55e 100644 --- a/fabric-ca-client/package.json +++ b/fabric-ca-client/package.json @@ -24,8 +24,7 @@ "sjcl-codec": "0.1.1", "url": "^0.11.0", "util": "^0.10.3", - "winston": "^2.2.0", - "x509": "^0.3.2" + "winston": "^2.2.0" }, "license": "Apache-2.0", "licenses": [ diff --git a/fabric-client/package.json b/fabric-client/package.json index 9e6e9925da..bdb6e8c97c 100644 --- a/fabric-client/package.json +++ b/fabric-client/package.json @@ -39,8 +39,7 @@ "tar-stream": "1.5.2", "url": "^0.11.0", "util": "^0.10.3", - "winston": "^2.2.0", - "x509": "^0.3.2" + "winston": "^2.2.0" }, "license": "Apache-2.0", "licenses": [ diff --git a/package.json b/package.json index 219a7e917a..b5e71be8de 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "tape": "^4.5.1", "tape-promise": "^1.1.0", "tar-fs": "^1.13.0", - "winston": "^2.2.0" + "winston": "^2.2.0", + "x509": "^0.3.2" }, "license": "Apache-2.0", "licenses": [ diff --git a/test/unit/fabric-ca-client.js b/test/unit/fabric-ca-client.js index c6593a8776..3580577b75 100644 --- a/test/unit/fabric-ca-client.js +++ b/test/unit/fabric-ca-client.js @@ -25,8 +25,9 @@ var utils = require('fabric-client/lib/utils.js'); var fs = require('fs'); var path = require('path'); var util = require('util'); +var rewire = require('rewire'); -var FabricCAServices = require('fabric-ca-client/lib/FabricCAClientImpl'); +var FabricCAServices = rewire('fabric-ca-client/lib/FabricCAClientImpl'); var FabricCAClient = FabricCAServices.FabricCAClient; const SAMPLE_PEM_ENCODED_CERTIFICATE = '-----BEGIN CERTIFICATE-----' + @@ -563,10 +564,28 @@ test('FabricCAServices: Test reenroll() function', function(t) { getSigningIdentity: function() {} }); }, - /Invalid enrollment certificate of the current user: does not contain the "CN" value/, + /Failed to parse the enrollment certificate of the current user for its subject/, 'Must throw error when current user enrollment certificate does not have a "CN" value' ); + var getSubjectCommonName = FabricCAServices.__get__('getSubjectCommonName'); + + t.throws( + () => { + getSubjectCommonName(CERT_WITHOUT_CN); + }, + /Certificate PEM does not seem to contain a valid subject with common name "CN"/, + 'Must throw error when target certificate does not contain a common name' + ); + + t.doesNotThrow( + () => { + getSubjectCommonName(VALID_CERT); + }, + null, + 'Must not throw error when target certificate is valid and contains a common name' + ); + t.doesNotThrow( () => { return cop.reenroll({