Use node-forge for PKCS#12 support. Remove need to write keys to file system.
authorBen Thompson <ben@xtuple.com>
Tue, 10 Jun 2014 23:03:57 +0000 (19:03 -0400)
committerBen Thompson <ben@xtuple.com>
Tue, 10 Jun 2014 23:03:57 +0000 (19:03 -0400)
node-datasource/routes/generate_oauth_key.js

index 44ad1a5..5e2637a 100644 (file)
@@ -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
     });
 
   };