Friday, October 25, 2013

SSL Configuration on Node.js for Server and Client sides

My previous post discussed how to simply generate a RSA private key and self-signed certificate. Those items will be used now to implement a node.js (utilizing express.js) server and client.

Server-side code

var fs = require('fs');
var https = require('https');
var express = require('express');

var appHttps = express();
var privateKey = fs.readFileSync('/sslcerts/key.pem'); //set path to your key
var certificate = fs.readFileSync('/sslcerts/cert.pem'); //set path to your cert
var credentials = {key: privateKey, cert: certificate};

var httpsServer = https.createServer(credentials, appHttps);
httpsServer.listen('8443');

//this is a framework for a REST interface
 appHttps.get('/ctispan/rest/key/:id',   
        function(req, res)
        {

                res.send(200,'hello world');
        });



Client-side code

This is written in a classical (as in class) type format.  Javascript isn't a class-type language, but my background is in Java which is.  Hence, I tend to mold things to what I'm comfortable with (classes).

var https = require('https');
var fs = require('fs');


function ClientRS(host, port, path)
{
    this.host = host;
    this.port = port;
    this.path = path;
 };

ClientRS.prototype.getValue = function(key, callback)
{
    var retVal='';
    var options = {
            host : this.host,
            port : this.port,
            path : this.path + "/key/" + key,
            ca: [fs.readFileSync(properties.sslHACert)],    //*see note below
            method: 'GET'
    };


var req = https.request(options, function(res) {
        console.log('GET status code: ', res.statusCode);
        res.on('data', function(chunk) {
            retVal += chunk;
        });
        res.on('end', function() {
            if (callback !== undefined)
                callback(retVal);
        });
    });
   
    req.end();

};

* That 'ca' line is necessary for self-signed certificates.  You need to tell node that the self-signed certificate is trusted (cause it shouldn't be in normal circumstances), otherwise you'll get thrown one of these beauties (that will terminate your client):

Error: DEPTH_ZERO_SELF_SIGNED_CERT


Invoking the Client code

callback = function(returnData) {
    console.log('in test client, returnData: ' + returnData);
};


var ClientRS = require('ClientRS');
client = new ClientRS('myhost', '8443', '/ctispan/rest');


client.getValue('111', callback);

Generating a SSL key + self-signed cert with openssl

Steps below for creating a PKI key + cert for your HTTPS implementation.  Linux command line is assumed.

Key

$ openssl genpkey -out key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048

Output of this is a 2048-bit private key (key.pem).


Certificate

$ openssl req -new -x509 -key key.pem -out cert.pem -days 9999

This takes the private key generated in the step above (key.pem) and creates a self-signed PKI certificate (cert.pem).  I put a expiration date of 9999 days on it.


Note - You generally want to use a resolvable DNS entry for the Common Name (CN) field in your certificate.  Putting an IP address in that field will bring you troubles from RFC 2818

In some cases, the URI is specified as an IP address rather than a
   hostname. In this case, the iPAddress subjectAltName must be present
   in the certificate and must exactly match the IP in the URI.
 
Net, you have to add a subjectAltName (SAN) field to your certificate with that same IP address.  I can attest that the Java Runtime Environment and node.js enforce this RFC clause.  You can avoid the entire problem by using a host name.

Installing Node.js on Redhat variants (rhel, centos, fedora)

No need for the source code download/compile cycle.

# yum install npm

Done.