Hostname / IP doesn't match certificate's altname


Question

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?

1
19
12/30/2012 6:30:42 AM

Accepted Answer

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 returning (not throwing) an error if there is a problem and undefined if there is it's valid.

16
2/29/2016 11:37:17 PM

In tls.js, lines 112-141, you can see that if the host name used when calling connect is an IP address, the certificate's CN is ignored and only the SANs are being used.

As my certificate doesn't use SANs, verification fails.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon