I am trying to create a TLS server / client setup using Node.js 0.8.8 with a self-signed certificate.
The essential server code looks like
var tlsServer = tls.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
}, function (connection) {
// [...]
});
tlsServer.listen(3000);
Now when I try to connect to this server I use the following code:
var connection = tls.connect({
host: '192.168.178.31',
port: 3000,
rejectUnauthorized: true,
ca: [ fs.readFileSync('server-cert.pem') ]
}, function () {
console.log(connection.authorized);
console.log(connection.authorizationError);
console.log(connection.getPeerCertificate());
});
If I remove the line
ca: [ fs.readFileSync('server-cert.pem') ]
from the client-side code, Node.js throws an error telling me DEPTH_ZERO_SELF_SIGNED_CERT
. As far as I understand it this is due to the fact that it is a self-signed cert and there is no other party who trusts this certificate.
If I remove
rejectUnauthorized: true,
as well, the error is gone - but connection.authorized
is equal to false
which effectively means that my connection is not encrypted. Anyway, using getPeerCertificate()
I can access the certificate sent by the server. As I want to enforce an encrypted connection, I understand that I may not remove this line.
Now I read that I can use the ca
property to specify any CA that I want Node.js to trust. The documentation of the TLS module implies that it's enough to add the server certificate to the ca
array, and then everything should be fine.
If I do that, this error is gone, but I get a new one:
Hostname/IP doesn't match certificate's altnames
To me this means that the CA is now basically trusted, hence that's okay now, but the certificate was made for another host than the one I use.
I created the certificate using
$ openssl genrsa -out server-key.pem 2048
$ openssl req -new -key server-key.pem -out server-csr.pem
$ openssl x509 -req -in server-csr.pem -signkey server-key.pem -out server-cert.pem
as the documentation implies. When creating the CSR I am asked the usual questions, such as for country, state, ... and common name (CN). As you are told "on the web" for an SSL certificate you do not provide your name as CN, but the host name you would like to use.
And this is probably where I fail.
I tried
localhost
192.168.178.31
eisbaer
eisbaer.fritz.box
where the last two are the local name and the fully qualified local name of my machine.
Any idea what I am doing wrong here?
Recently there was an addition to node.js which allows overriding hostname check with a custom function. It was added to v0.11.14 and will be available in the next stable release (0.12). Now you can do something like:
var options = {
host: '192.168.178.31',
port: 3000,
ca: [ fs.readFileSync('server-cert.pem') ],
checkServerIdentity: function (host, cert) {
return undefined;
}
};
options.agent = new https.Agent(options);
var req = https.request(options, function (res) {
//...
});
This will now accept any server identity, but still encrypt the connection and verify keys.
Note that in previous versions (e.g. v0.11.14
), the checkServerIdentity
was to return a boolean
indicating the validity of the server. That has been changed (before v4.3.1
) to the function return
ing (not throw
ing) an error if there is a problem and undefined
if there is it's valid.