Nodejs problem with IPv6 subjectAltNames

Background

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 '['...

Reproducing the problem

This repository contains code to reproduce the problem. Run it with

./generate-certificates
node index.js

It will print the following result

Test 1 failed: Shall connect to ::1. 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
Test 2 ok: Shall connect to 127.0.0.1

The test will also leave the server running, so you can check that curl accepts the certificate provided by the server when you give curl the ca-certificate generated by generate-certificates:

curl --cacert ca.pem https://[::1]:3000

Note that curl on OS X does not support the cacert-parameter (curl/curl#976). If you want to test with curl, try linux instead.