Node.JS + HTTPS + Client Cert = Problems

certificate, client, https, node.js, ssl

Solution

How about putting this server behind an nginx?

This might sound complicated, or add a lot of overhead, but I promise you it's REALLY simple, and nginx's SSL handling is easy.

Look for proxying with nginx (example).

P.S Both apps can, and probably, reside in the same server machine.

Problem

I've been trying to get Node.JS to work with SSL and client certificates. Originally, I had tried to get it to work with restify (see my question here). When I couldn't get that to work, I backed up and tried to find an example that illustrated what I was trying to accomplish. I tried this one, and I'm receiving an odd error. Code is as follows: Server: ``` var sys = require("sys"); var fs = require("fs"); var https = require("https"); var options = { key: fs.readFileSync("../certs/server.key"), cert: fs.readFileSync("../certs/server.crt"), ca: fs.readFileSync("../certs/ca.crt"), requestCert: true, rejectUnauthorized: true }; https.createServer(options, function (req, res) { console.log(req); res.writeHead(200); sys.puts("request from: " + req.connection.getPeerCertificate().subject.CN); res.end("Hello World, " + req.connection.getPeerCertificate().subject.CN + "\n"); }).listen(8080); sys.puts("server started"); ``` Client: ``` var https = require('https'); var fs = require("fs"); var options = { host: 'localhost', port: 8080, path: '/hello', method: 'GET', key: fs.readFileSync("../certs/user.key"), cert: fs.readFileSync("../certs/user.crt"), ca: fs.readFileSync("../certs/ca.crt"), passphrase: 'thepassphrase' }; var req = https.request(options, function(res) { console.log("statusCode: ", res.statusCode); console.log("headers: ", res.headers); res.on('data', function(d) { process.stdout.write(d); }); }); req.end(); req.on('error', function(e) { console.error(e); }); ``` Running test-client.js yields this: ``` { [Error: socket hang up] code: 'ECONNRESET' } ``` Attempting the same sort of thing with curl: ``` curl -k -v --key user.key --cert user.crt:thepassphrase --cacert ca.crt https://localhost:8080/hello ``` yields: ``` * About to connect() to localhost port 8080 (#0) * Trying 127.0.0.1... connected * successfully set certificate verify locations: * CAfile: ca.crt CApath: /etc/ssl/certs * SSLv3, TLS handshake, Client hello (1): * SSLv3, TLS handshake, Server hello (2): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Request CERT (13): * SSLv3, TLS handshake, Server finished (14): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Client key exchange (16): * SSLv3, TLS handshake, CERT verify (15): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * Unknown SSL protocol error in connection to localhost:8080 * Closing connection #0 curl: (35) Unknown SSL protocol error in connection to localhost:8080 ``` If I want to go the extra step to require a client certificate, how would I go about it?

Original source