nodejs/node

tls client does not accept certificates with IPv6 subjectAltNames

mattiash opened this issue · 1 comments

  • Version: 6.3.1 and 8.2.1 (at least)
  • Platform: OS X and Linux (at least)
  • Subsystem: tls

TL;DR

Node's tls client does not accept certificates signing IPv6 addresses, only certificates with IPv4 addresses are accepted. Both IPv6 and IPv4 should be accepted.

Long version

It is possible to generate TLS certificates that sign IP addresses if you add the IP addresses as alternative names for the certificate. This allows you to connect to url:s such as https://12.34.56.78 if the server at that IP address has a certificate that signs that IP address.

It is as far as I know not possible to buy a certificate that signs an IP address from the usual certificate vendors. It is however possible to use such certificates if you have your own CA and explicitly trust certificates signed by your own CA. This is very useful for clustering solutions where DNS only complicates the design.

Node has support for everything described above as long as you use IPv4 addresses. However, if you use IPv6 addresses it does not work as expected. If you try to connect to https://[::1], the node https-client refuses to accept the certificate with the error

Host: [. is not in the cert's altnames: IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1

Note that the error message specifically says that the certificate includes the ip-address 0:0:0:0:0:0:0:1 (which is another representation of ::1), so it should accept the certificate.

The code that builds the error-message is in tls.js https://github.com/nodejs/node/blob/master/lib/tls.js#L216 :

reason = `Host: ${host}. is not in the cert's altnames: ${altNames}`

The code seems to think that the hostname in the url is '['...

I have written code to reproduce the problem in https://github.com/mattiash/test-checkServerIdentity

With the following patch

diff --git a/lib/_http_agent.js b/lib/_http_agent.js
index ddd36c158e..5b0cc1d7ab 100644
--- a/lib/_http_agent.js
+++ b/lib/_http_agent.js
@@ -152,7 +152,7 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
     options.servername = options.host;
     const hostHeader = req.getHeader('host');
     if (hostHeader) {
-      options.servername = hostHeader.replace(/:.*$/, '');
+//      options.servername = hostHeader.replace(/:.*$/, '');
     }
   }

the error-message printed by the testcase changes to

Test 1 failed: Shall connect to ::1. Error:  IP: ::1 is not in the cert's list: 127.0.0.1, 0:0:0:0:0:0:0:1

Now we also need to change all IPv6 addresses into some canonical format before the comparison. And do a proper patch for _http_agent that doesn't break other stuff...