From d21243be2a073b5f11b6849ff6de28bbaa69c0ca Mon Sep 17 00:00:00 2001 From: Ben Thompson Date: Tue, 10 Jun 2014 19:03:57 -0400 Subject: [PATCH] Use node-forge for PKCS#12 support. Remove need to write keys to file system. --- node-datasource/routes/generate_oauth_key.js | 96 ++++++++------------ 1 file changed, 37 insertions(+), 59 deletions(-) diff --git a/node-datasource/routes/generate_oauth_key.js b/node-datasource/routes/generate_oauth_key.js index 44ad1a5b8..5e2637a05 100644 --- a/node-datasource/routes/generate_oauth_key.js +++ b/node-datasource/routes/generate_oauth_key.js @@ -9,6 +9,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */ var ursa = require("ursa"), exec = require("child_process").exec, + forge = require("node-forge"), spawn = require("child_process").spawn, async = require("async"), path = require("path"), @@ -27,70 +28,47 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */ console.log("oauth2client error ", arguments); res.send({isError: true, error: err}); }, - convertToP12 = function (publicKey, privateKey) { - var filenamePrefix = path.join("/tmp/temp_" + id), - publicKeyFilename = filenamePrefix + "_public_key.pem", - csrFilename = filenamePrefix + "_csr.pem", - certFilename = filenamePrefix + "_cert.pem", - privateKeyFilename = filenamePrefix + "_private_key.pem", - p12Filename = filenamePrefix + ".p12", - attachmentFilename = publicKey.substring(0, publicKey.indexOf("-----END")) + ".p12", - csrExec = "openssl req -new -key %@ -out %@".f(privateKeyFilename, csrFilename), - p12contents; - - async.series([ - function (callback) { fs.writeFile(publicKeyFilename, publicKey, callback); }, - function (callback) { fs.writeFile(privateKeyFilename, privateKey, callback); }, - function (callback) { - var child = exec(csrExec, callback); - // blow through command-line questions - child.stdin.setEncoding = 'utf-8'; - child.stdin.write("\n\n\n\n\n\n\n\n\n"); - //child.stdin.write("US\nVirginia\nNorfolk\nxTuple\n\n\n\n\n"); - child.stdin.end(); - }, - function (callback) { - var certSpawn = spawn("openssl", - ["x509", "-req", "-in", csrFilename, "-signkey", privateKeyFilename, "-out", certFilename]); - certSpawn.on('close', function (code) { - callback(null, code); - }); - }, - function (callback) { - var child = spawn("openssl", - ["pkcs12", "-export", "-in", certFilename, "-inkey", privateKeyFilename, "-out", p12Filename, "-password", "pass:notasecret"]); - - child.on('close', function (code) { - callback(null, code); - }); - }, - function (callback) { - fs.readFile(p12Filename, function (err, contents) { - p12contents = contents; - callback(err, contents); - }); - }, - function (callback) { fs.unlink(publicKeyFilename, callback); }, - function (callback) { fs.unlink(privateKeyFilename, callback); }, - function (callback) { fs.unlink(csrFilename, callback); }, - function (callback) { fs.unlink(certFilename, callback); }, - function (callback) { fs.unlink(p12Filename, callback); } - ], - function (err, results) { + genKey = function (model, result) { + /** + * This is REALLY slow in pure javascript. ursa is much faster. + * @See: https://github.com/digitalbazaar/forge/issues/125 + forge.pki.rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) { if (err) { - res.send({isError: true, message: "Error generating p12 key: " + err.message, error: err}); + res.send({isError: true, message: "Error generating keypair: " + err.message, error: err}); return; } - res.attachment(attachmentFilename); - res.send(new Buffer(p12contents)); + + console.log("1.2: ", new Date().getTime()); + fetchSuccess(model, result, keypair); }); + */ + + // Use ursa for the key gen and then convert to forge's format. + var keypair = ursa.generatePrivateKey(); + var keys = { + privateKey: forge.pki.privateKeyFromPem(keypair.toPrivatePem().toString()), + publicKey: forge.pki.publicKeyFromPem(keypair.toPublicPem().toString()) + }; + + fetchSuccess(model, result, keys); + }, + sendP12 = function (keys) { + // It's possible and much easier to generate the p12 file without a + // cert. This example shows how to generate a cert if we actually need + // to, but OAuth is working without. + // @see: https://github.com/digitalbazaar/forge/blob/master/tests/nodejs-create-pkcs12.js#L11 + //var p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert], 'notasecret'), + var p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, null, 'notasecret'), + p12Der = forge.asn1.toDer(p12Asn1).getBytes(), + buffer = new Buffer(p12Der, 'binary'); + + res.attachment(clientModel.get('clientName') + '.p12'); + res.send(new Buffer(buffer, 'base64')); }, - fetchSuccess = function (model, result) { - var keypair = ursa.generatePrivateKey(), - privateKey = keypair.toPrivatePem().toString(), - publicKey = keypair.toPublicPem().toString(), + fetchSuccess = function (model, result, keys) { + var publicKey = forge.pki.publicKeyToPem(keys.publicKey), saveSuccess = function (model, result) { - convertToP12(publicKey, privateKey); + sendP12(keys); }; // Cursory validation: this should be a jwt bearer and the @@ -115,7 +93,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */ username: req.session.passport.user.username, database: req.session.passport.user.organization, error: error, - success: fetchSuccess + success: genKey }); }; -- 2.39.2