Skip to content

Commit

Permalink
[FABN-897] fabric-ca-client enroll with csr
Browse files Browse the repository at this point in the history
allow the fabric-ca-client to accept a csr at enroll()

Change-Id: I9f9645a9c4c4592ff2d644982eda139b66329079
Signed-off-by: zhaochy <[email protected]>
  • Loading branch information
zhaochy1990 committed Nov 23, 2018
1 parent 83fedc9 commit 54fa5cf
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 79 deletions.
124 changes: 65 additions & 59 deletions fabric-ca-client/lib/FabricCAServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ const FabricCAServices = class extends BaseClient {
* @property {string} enrollmentSecret - The secret associated with the enrollment ID
* @property {string} profile - The profile name. Specify the 'tls' profile for a TLS certificate;
* otherwise, an enrollment certificate is issued.
* @property {Buffer} csr - Optional. Certificate Signing Request. The message sent from client side to
* Fabric-ca for the digital identity certificate.
* @property {AttributeRequest[]} attr_reqs - An array of {@link AttributeRequest}
*/

Expand All @@ -153,76 +155,80 @@ const FabricCAServices = class extends BaseClient {
* @param req the {@link EnrollmentRequest}
* @returns Promise for an object with "key" for private key and "certificate" for the signed certificate
*/
enroll(req) {
const self = this;
async enroll(req) {
if (!req) {
logger.error('enroll() missing required argument "request"');
throw new Error('Missing required argument "request"');
}

return new Promise((resolve, reject) => {
if (!req) {
logger.error('enroll() missing required argument "request"');
return reject(new Error('Missing required argument "request"'));
}
if (!req.enrollmentID) {
logger.error('Invalid enroll request, missing enrollmentID');
return reject(new Error('req.enrollmentID is not set'));
}
if (!req.enrollmentID) {
logger.error('Invalid enroll request, missing enrollmentID');
throw new Error('req.enrollmentID is not set');
}

if (!req.enrollmentSecret) {
logger.error('Invalid enroll request, missing enrollmentSecret');
return reject(new Error('req.enrollmentSecret is not set'));
}
if (!req.enrollmentSecret) {
logger.error('Invalid enroll request, missing enrollmentSecret');
throw new Error('req.enrollmentSecret is not set');
}

if (req.attr_reqs) {
if (!Array.isArray(req.attr_reqs)) {
logger.error('Invalid enroll request, attr_reqs must be an array of AttributeRequest objects');
return reject(new Error('req.attr_reqs is not an array'));
} else {
for (const i in req.attr_reqs) {
const attr_req = req.attr_reqs[i];
if (!attr_req.name) {
logger.error('Invalid enroll request, attr_reqs object is missing the name of the attribute');
return reject(new Error('req.att_regs is missing the attribute name'));
}
if (req.attr_reqs) {
if (!Array.isArray(req.attr_reqs)) {
logger.error('Invalid enroll request, attr_reqs must be an array of AttributeRequest objects');
throw new Error('req.attr_reqs is not an array');
} else {
for (const i in req.attr_reqs) {
const attr_req = req.attr_reqs[i];
if (!attr_req.name) {
logger.error('Invalid enroll request, attr_reqs object is missing the name of the attribute');
throw new Error('req.att_regs is missing the attribute name');
}
}
}
}

// generate enrollment certificate pair for signing
let opts;
if (self.getCryptoSuite()._cryptoKeyStore) {
opts = {ephemeral: false};
let opts;
if (this.getCryptoSuite()._cryptoKeyStore) {
opts = {ephemeral: false};
} else {
opts = {ephemeral: true};
}

try {
let csr;
let privateKey;
if (req.csr) {
logger.debug('try to enroll with a csr');
csr = req.csr;
} else {
opts = {ephemeral: true};
try {
privateKey = await this.getCryptoSuite().generateKey(opts);
logger.debug('successfully generated key pairs');
} catch (err) {
throw new Error(util.format('Failed to generate key for enrollment due to error [%s]', err));
}
try {
csr = privateKey.generateCSR('CN=' + req.enrollmentID);
logger.debug('successfully generated csr');
} catch (err) {
throw new Error(util.format('Failed to generate CSR for enrollmemnt due to error [%s]', err));
}
}
self.getCryptoSuite().generateKey(opts)
.then(
(privateKey) => {
// generate CSR using enrollmentID for the subject
try {
const csr = privateKey.generateCSR('CN=' + req.enrollmentID);
self._fabricCAClient.enroll(req.enrollmentID, req.enrollmentSecret, csr, req.profile, req.attr_reqs)
.then(
(enrollResponse) => {
return resolve({
key: privateKey,
certificate: enrollResponse.enrollmentCert,
rootCertificate: enrollResponse.caCertChain
});
},
(err) => {
return reject(err);
}
);

} catch (err) {
return reject(new Error(util.format('Failed to generate CSR for enrollmemnt due to error [%s]', err)));
}
},
(err) => {
return reject(new Error(util.format('Failed to generate key for enrollment due to error [%s]', err)));
}
);
const enrollResponse = await this._fabricCAClient.enroll(req.enrollmentID, req.enrollmentSecret, csr, req.profile, req.attr_reqs);
logger.debug('successfully enrolled %s', req.enrollmentID);

});
const enrollment = {
certificate: enrollResponse.enrollmentCert,
rootCertificate: enrollResponse.caCertChain,
};
if (!req.csr) {
enrollment.key = privateKey;
}
return enrollment;
} catch (error) {
logger.error('Failed to enroll %s, error:%o', req.enrollmentID, error);
throw error;
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions scripts/check_license.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function filterExcludedFiles {
| grep -v "\.gradle$" \
| grep -v "\.cds$" \
| grep -v "\.jar$" \
| grep -v "\.csr$" \
| sort -u`
}

Expand Down
16 changes: 16 additions & 0 deletions test/fixtures/fabricca/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
this folder contains the test fixtures for fabric ca integration tests.

The file test.csr and test.key are generated for testing enroll a user with a csr

test.csr is the certificate signing request to be sent to fabirc-ca by the API: FabricCAService.enroll()
test.key is the corresponding private key for this user

The test.key and test.csr are generated by this `openssl` command.
```
openssl req -nodes -newkey rsa:2048 -keyout test.key -out test.csr
```

The necessary information for creating this CSR is:

Common Name: aTestUser

18 changes: 18 additions & 0 deletions test/fixtures/fabricca/test.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICzjCCAbYCAQAwgYgxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlTaGFuZyBIYWkx
EjAQBgNVBAcMCVNoYW5nIEhhaTETMBEGA1UECgwKVW1hcmtjbG91ZDEMMAoGA1UE
CwwDRGV2MRIwEAYDVQQDDAlhVGVzdFVzZXIxGjAYBgkqhkiG9w0BCQEWC3h4eEB4
eHguY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/5e1+PT4ywm
psBhtjVYeGm72B7G2I2+vPhZ2bDiS7kWJ00Eaw9S6xGGh28zXMLoW1ABQzj6J0eb
iqXe6proIhGB4E24KBzhx6ZGVzccaoVEoGNq0OQV98C/jhZ0RrcubxKFT9TC8h3V
VUpdsQP2D3QQgUjwUqUsFAZtg/qn2wtydWCw9FTfnUCUwfhfawS6pPxF3MLXN4Re
iUWZeUunmTFGBX1cnOph1hZd8MLXDKfjvpibpnJO+KUWAWiejzije1TMAT09s9ln
wA7U4t8+wEaxPpdUz8E0M60W8t8p3KSvy38at1hDA8Qa3PaT54NJgH3HHVSoyg19
qYC1O03BrQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAIKZUvnzOwRoP3WBIcON
T6F3PVDpWSRL0T1RG6RqMs1+mTTfwBDWmWQcdEIgj5+YaF3UmdxFos82U3kSxIaT
wnbLo/hLj9HTNknihFMTopS67BaLyPXdiR13d9YTBZYar831gR8MHKBDCRVFjCYL
zRIZ1NvK/Tv+eeQ4CazCWEZ1yryasjibq8tZ5ByfC0bC0dzDhv317VTXhwK+RKXH
eH4Ne4bxWWJFszyWEFvl3Q5Z02+SxtcU8Nsgr5rnUsOIO7jH+lOGrjZJX2s8oc5Q
+gZKdFkAx7GQUMjgHdOF9MV0ckgRrmPT/ymkVPEz/rKSA/O0AiQgipeWIPLPsgGN
gAY=
-----END CERTIFICATE REQUEST-----
28 changes: 28 additions & 0 deletions test/fixtures/fabricca/test.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCr/l7X49PjLCam
wGG2NVh4abvYHsbYjb68+FnZsOJLuRYnTQRrD1LrEYaHbzNcwuhbUAFDOPonR5uK
pd7qmugiEYHgTbgoHOHHpkZXNxxqhUSgY2rQ5BX3wL+OFnRGty5vEoVP1MLyHdVV
Sl2xA/YPdBCBSPBSpSwUBm2D+qfbC3J1YLD0VN+dQJTB+F9rBLqk/EXcwtc3hF6J
RZl5S6eZMUYFfVyc6mHWFl3wwtcMp+O+mJumck74pRYBaJ6POKN7VMwBPT2z2WfA
DtTi3z7ARrE+l1TPwTQzrRby3yncpK/Lfxq3WEMDxBrc9pPng0mAfccdVKjKDX2p
gLU7TcGtAgMBAAECggEAV8pjRvd3tEFZCUyxk3vvSKdPxqUPAoJ3laV+12s+V0CG
WlEprCwKOwqbDI5qPTMPv2w6+MEgjuQyNLct7bpcOueMz51cHr1/UZVB2kq7KnQT
73pPj4ic3fV40WIMv/vIq9FcUr9bMpKfzgVDhZNsCb9InXVJliXRNfeB9xf+jgOX
grezvIjQYxud0XcBl93XVJETiswplsUxKN0IsGpPwakQL4Dw0C0HKQYV9nkAYwCX
OssrZbzw/9AiSpxJI/cL9Kj39tiXDQd5Y+NLG77OXYd1erNWWU8JiCXlqOni3ZRe
ystjG+H/L2ykrVMxzs7CbjRwOxrcxZJjQSDoWHaUAQKBgQDXe88mS/jOGfen4hAi
jM8y0TZV5+udIv/W6DFSCBGrHsVzRRHWFxoInSqOxAVqnMSQeyPEWKH7KoT2Pds/
zremYa6KDb1fbrA4PLL9tyJtHxRGl/dGWFGDEld+ftyjsstQ0h81ia6mO8wv1l/X
0GNH8ubphpN3OSFw4W6/FEhjbQKBgQDMVTF+/jgnlX+W6R1ivhJlMA/0Nlm/0/AR
HkLy8dyXN5dOsPvDH74cA+SvRhgCpTR1PxdeagECML7bU8WLEyb2Vl/ooS6khn8A
Gf2JHr5s0zOOioSe+eulE2FOQtoehMUK3IW3RHWUsF5fVSM+976Py44AF0wbfgLJ
n+hCDqmvQQKBgH/kYtk3BA/HR77jpHHtItjlZRttj+DusevqWcN3OI1YsRYuK1zm
zzPHnEepvhE9xEiAXYUemd0Jqmq/4c4oMcXVehND/l6SwpJOLGn7dLpRVOZlMYUV
zCNAEp/oSan650MrwwwykG7nVNQbpzze2N4OI9D8Z6++P8PQeLt+HUytAoGAZoH+
2MTUixc28RB1y4mrVNQtzMhPciry7sONtq+biMk8wR+MzjDogMl7OmshtIIItSE3
qgfoBZ9KwGFgYwryqvZeGyhxa9yFbhmb1eR0h8fUv5fCPLcIfsIgONDU+CaHa5GC
C5Tun+9zCMR/cCF9mkn2LRmC9u/amif8rtcITYECgYA92PDXURfoQMFBHIPZ01bO
G27pi4YAhOSiAvr58w46okqOzCD+gS7hDw3jZOl/y2j+3N1hKkhWqY5mSZhT4eQx
UbAb9jUYE3//CjeK1vKFTknjQjUrD5Z3JD6Dh2RMT4olJuYSRtyjACS5CccUclDy
IhR+2kbCBC2gkX7c8TKuZw==
-----END PRIVATE KEY-----
88 changes: 68 additions & 20 deletions test/integration/fabric-ca-services-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,7 @@ const tlsOptions = {

test('\n\n ** FabricCAServices: Test enroll() With Dynamic CSR **\n\n', (t) => {
testUtil.resetDefaults();
FabricCAServices.addConfigFile(path.join(__dirname, 'e2e', 'config.json'));
ORGS = FabricCAServices.getConfigSetting('test-network');
fabricCAEndpoint = ORGS[userOrg].ca.url;

FabricCAServices.getConfigSetting('crypto-keysize', '256');// force for gulp test
FabricCAServices.setConfigSetting('crypto-hash-algo', 'SHA2');// force for gulp test

const caService = new FabricCAServices(fabricCAEndpoint, tlsOptions, ORGS[userOrg].ca.name);
const caService = getFabricCAService(userOrg);

const req = {
enrollmentID: 'admin',
Expand Down Expand Up @@ -398,18 +391,73 @@ test('\n\n ** FabricCAClient: Test enroll With Static CSR **\n\n', (t) => {
});
});

// function savePem(pem) {
// logger.info(' saving :: %j',pem);
// let file_path = path.join(__dirname, '../attribute.pem');
// fs.writeFileSync(file_path, pem);
// }
//
// function readPem() {
// logger.info(' reading pem');
// let file_path = path.join(__dirname, '../attribute.pem');
// var pem = fs.readFileSync(file_path);
// return pem;
// }
test('\n\n ** FabricCAClient: Test enroll With a Signed CSR **\n\n', async (t) => {
try {
testUtil.resetDefaults();
const caService = getFabricCAService();
const admin = await enrollAdminTest(caService, t);

const newUser = {
enrollmentID: 'aTestUser',
maxEnrollments: -1,
enrollmentSecret: 'userpw',
};
await caService.register(newUser, admin);

const myCsr = fs.readFileSync(path.resolve(__dirname, '../fixtures/fabricca/test.csr'), 'utf8');

const req = {
enrollmentID: newUser.enrollmentID,
enrollmentSecret: newUser.enrollmentSecret,
csr: myCsr,
};

const enrollment = await caService.enroll(req);
t.pass('Successfully get enrollment by csr');

// check that we got back the expected certificate
let subject;
try {
subject = X509.getSubject(FabricCAServices.normalizeX509(enrollment.certificate));
} catch (err) {
t.fail(util.format('Failed to parse enrollment cert\n%s\n. Error: %s', enrollment.certificate, err));
}

t.equal(subject.commonName, req.enrollmentID, 'Subject should be /CN=' + req.enrollmentID);
t.pass('Successfully tested enroll with csr');
} catch (error) {
t.fail(error.message);
t.end();
}
});

function getFabricCAService() {
FabricCAServices.addConfigFile(path.join(__dirname, 'e2e', 'config.json'));
ORGS = FabricCAServices.getConfigSetting('test-network');
fabricCAEndpoint = ORGS[userOrg].ca.url;

FabricCAServices.getConfigSetting('crypto-keysize', '256');// force for gulp test
FabricCAServices.setConfigSetting('crypto-hash-algo', 'SHA2');// force for gulp test

return new FabricCAServices(fabricCAEndpoint, tlsOptions, ORGS[userOrg].ca.name);
}

async function enrollAdminTest(caService, t) {
try {
const req = {
enrollmentID: 'admin',
enrollmentSecret: 'adminpw'
};
const enrollment = await caService.enroll(req);
const admin = new User('admin1');
await admin.setEnrollment(enrollment.key, enrollment.certificate, 'Org1MSP');
t.pass('Successfully enrolled admin');
return admin;
} catch (error) {
t.fail(error.message);
t.end();
}
}

async function timeOutTest(signingIdentity, t) {
const CONNECTION_TIMEOUT = FabricCAServices.getConfigSetting('connection-timeout');
Expand Down

0 comments on commit 54fa5cf

Please sign in to comment.