Skip to content

Commit

Permalink
[FAB-4847] remove node-x509 dependency
Browse files Browse the repository at this point in the history
This module causes problems running on windows and prevents
'sudo npm install' on CentOS and RHEL

The fix is to replace that with own impl of parsing an ECDSA
cert to get the subject CN based on jsrsasign.X509 and
jsrsasign.ASN1HEX utilities

Change-Id: I8c9eda208689e310f8a188049839dae813312ae6
Signed-off-by: Jim Zhang <jzhang@us.ibm.com>
  • Loading branch information
jimthematrix committed Jul 20, 2017
1 parent cc5356b commit 34dd649
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 15 deletions.
30 changes: 22 additions & 8 deletions fabric-ca-client/lib/FabricCAClientImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
3 changes: 1 addition & 2 deletions fabric-ca-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
3 changes: 1 addition & 2 deletions fabric-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
23 changes: 21 additions & 2 deletions test/unit/fabric-ca-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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-----' +
Expand Down Expand Up @@ -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({
Expand Down

0 comments on commit 34dd649

Please sign in to comment.