1

Here's the situation. I created a self-signed CA certificate, and used it to sign a second certificate for use with https. The web server is nginx doing SSL termination and reverse proxying to an expressjs application. To verify that the chain of trust is correct, I installed the CA in Firefox and was able to access the website over https without warnings, as expected. Further, I can inspect the server's certificate with openssl x509 -in server.crt -text -noout, and I see both the expected issuer and in particular the expected common name for the subject. (Note: the common name being used here is an IP address, in case that might cause trouble.)

However, when I try to access the server via a nodejs script using requestjs, things don't go so smoothly. In the script, the CA certificate is loaded using code like this:

request.get({url: theUrl, ca: fs.readFileSync("./ca.crt")}, ...

But I get this error (line broken for readability, original is one line):

Contacting doorman failed: Error: Hostname/IP doesn't match certificate's altnames: 
"IP: <redacted> is not in the cert's list: "

It's particularly suspicious that it seems to be saying that the "cert's list" is empty. Using rejectUnauthorized: false in the options has been suggested in other answers, but it isn't a great option for this application because I want to have the identity validation.

How can I make requestjs/nodejs trust this certificate?


Contents of the server certificate, as reported by openssl x509 -text

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 3 (0x3)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=State, L=City, O=Company, CN=www.company.com
        Validity
            Not Before: Dec  7 17:19:51 2015 GMT
            Not After : Oct 14 17:19:51 2025 GMT
        Subject: C=US, ST=State, L=City, O=Company, CN=1.2.3.4/emailAddress=webmaster@company.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

And the config file used to generate that certificate:

[ req ]
default_bits       = 4096
prompt             = no
encrypt_key        = no

distinguished_name = req_distinguished_name
req_extensions     = v3_req

[ req_distinguished_name ]
countryName            = "US"
stateOrProvinceName    = "State"
localityName           = "City"
organizationName       = "Company"
commonName             = "1.2.3.4"
emailAddress           = "webmaster@company.com"

[ v3_req ]
subjectAltName = IP:1.2.3.4
Fizzbuzz97
  • 719
  • 1
  • 6
  • 14
  • There is a section in the docs about self signed certs, not sure if this is the same issue: https://github.com/request/request#using-optionsagentoptions – Mike D Dec 07 '15 at 16:39

1 Answers1

3

Hostname/IP doesn't match certificate's altnames:

My guess is that your certificate contains the correct hostname used in the URL only as subject (CN) and not as subject alternative name (SAN), but that it contains other names as SAN. The standard is quite clear that if any subject alternative DNS names are given the CN should not be checked, but most browsers check CN anyway (I think Safari is more strict). But node.js implements the strict behavior and will thus fail.

If my guess is right (hard to see without certificate) then the issue has to be fixed by creating a correct certificate. Another possibility is that you use a slightly different URL in browser and nodejs (like with and without www prefix).

EDIT: after seeing what certificate you actually use....

You have a certificate with an IP address in the CN. While this is also supported by most browsers it is not right. An IP address must be given as ipadress SAN entry and not as CN. Nodejs expects it only there and I think that Safari is also that strict. Note that for IE you have to put it either into CN or as dnsname SAN entry just because they like to behave contrary to the standard in this case too. Thus to be on the safe side put it as ipaddress, dnsname and maybe as CN too.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • I hadn't specified any SAN when creating the certificate, so this seems pretty likely to me. Is it possible to specify that on the command line with the -subj switch? I tried something like this: `openssl req -new -key server.key -out server.csr -subj "/C=US/ST=State/L=City/O=Company Name/CN=1.2.3.4/subjectAltName=IP Address=1.2.3.4"`, but it looks like the SAN info is just getting concatenated into the CN field. – Fizzbuzz97 Dec 07 '15 at 17:01
  • @Fizzbuzz97: it can not be easily done on the command line, but there might be one lurking in your configuration. See http://apetec.com/support/GenerateSAN-CSR.htm for how to create certificates with SAN. Apart from that - if you would show the details of your certificate (`openssl x509 -text`) one could better see what's really the case. For now it is just guessing. – Steffen Ullrich Dec 07 '15 at 17:21
  • I tried using a config file with a `[ v3_req ]` section to provide subjectAltName, but it didn't help. I've edited in the certificate contents above. I think the problem might be that it's a producing a v1 certificate rather than a v3 certificate, but a quick google search hasn't shown me how one might make that choice. – Fizzbuzz97 Dec 07 '15 at 17:50
  • @Fizzbuzz97: using an IP address in the certificate is special again, see edit. And I don't recommend to use X509v1 any longer, there are just reaaaly old. At least for your use case you need X509v3 to have a SAN section. – Steffen Ullrich Dec 07 '15 at 18:04
  • As per [this question](http://stackoverflow.com/questions/26788244/how-to-create-a-legacy-v1-or-v2-x-509-cert-for-testing) my understanding is that openssl will automatically generate a v1 certificate if it doesn't think you need any extensions. With the .cfg file I'm using (also edited in above), it seems like it should be adding an extensions section, but it's not... – Fizzbuzz97 Dec 07 '15 at 18:19
  • @Fizzbuzz97: are you sure it even looks at the config file? How did you specify to use this specific config file? – Steffen Ullrich Dec 07 '15 at 19:37
  • I was assuming that extension data in the csr would carry over into the cert when using `openssl x509 ...`, but it seems this is not the case. I started from scratch using `openssl ca ...` instead, where copying over the csr extension data can be explicitly specified in the config file used during the signing. Now things are working correctly. If it's possible to do that with `x509`, I couldn't figure it out from reading the docs. Thanks so much for your help tracking this down! – Fizzbuzz97 Dec 08 '15 at 16:04