Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nodejs ssl "unable to get local issuer certificate"

Tags:

node.js

https

ssl

I've been given an SSL cert to use for signing client requests, as well as the relevant CA certs. I can verify it using openssl:

$ openssl s_client -CAfile /etc/ssl/foo/ca-combined.pem -servername foo.co.in -connect foo.co.in:443
CONNECTED(00000003)
... snip ...
Verify return code: 0 (ok)
---
closed

(I mashed the 2 CA certs into one file). But when I try to replicate it using node:

    var tls = require('tls');
    var fs = require('fs');

    var options = { 
        host: 'foo.co.in',
        servername: 'foo.co.in',
        port: 443,
        key: fs.readFileSync('/etc/ssl/private/foo.key'),
        cert: fs.readFileSync('/etc/ssl/foo/cert.pem'),
        ca: [fs.readFileSync('/etc/ssl/foo/combined-ca.pem')]
    };  

    tls.connect(options, function(err) {
        done(err);
    });

I get an error:

Uncaught Error: unable to get local issuer certificate
  at Error (native)
  at TLSSocket.<anonymous> (_tls_wrap.js:1092:38)
  at TLSSocket._finishInit (_tls_wrap.js:610:8)
  at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:440:38)

I found an answer on here, suggesting that I need to put each CA cert in a separate file:

ca: [fs.readFileSync('/etc/ssl/foo/ca.pem'), fs.readFileSync('/etc/ssl/foo/root-ca.pem')]

but that still gave the same error. (I also tried reversing the order). I then tried putting the intermediate cert in with the client one, and just providing the root CA cert as ca (which seems to be what the docs suggest), same error. At this point I'm running out of ideas. The fact that openssl is happy suggests that I'm doing something wrong, any suggestions?

$ node --version
v6.10.1

(I realise I can set rejectUnauthorized to false, but I'd really rather not)

like image 963
grahamrhay Avatar asked Apr 05 '17 09:04

grahamrhay


3 Answers

To solve this generally for you system you have 2 different options:

The first is to export the variable NODE_EXTRA_CA_CERTS=$PATH_TO_YOUR_CUSTOM_CA_FILE.pem pointing to your custom CA_FILE

The second is to run node with the option --use-openssl-ca like:

node --use-openssl-ca main.js

This assumes that you already configured your ssl certificates with something like:

wget -qP /usr/local/share/ca-certificates http://your_local_cert/local_ca.pem\
    && update-ca-certificates

See https://github.com/microsoft/vscode-remote-release/issues/5620

like image 148
Alex Avatar answered Oct 06 '22 21:10

Alex


Turns out that I didn't need to supply the CA cert, as their CA was properly signed by a "known" authority. So I could just remove the ca field from my request.

like image 39
grahamrhay Avatar answered Oct 06 '22 21:10

grahamrhay


I created a root-ca and a intermediate-ca (signed by root-ca) certificates, then I created a server and a client certificates signed by intermediate-ca. To test it I've been implementing a server and client https with nodejs so a set it up with the certs and a get the following error on my client:

problem with request: unable to get local issuer certificate

To solve it I need to put in my nodejs codes, at ca field, my root-ca and intermediate-ca certs. Like that:

key: fs.readFileSync('path/client.privkey.pem'),
cert: fs.readFileSync('path/client.cert.pem'),
ca: [ fs.readFileSync('path/intermed-ca.cert.pem'), fs.readFileSync('path/root-ca.cert.pem') ],

it works for me.

like image 33
Jeff Pal Avatar answered Oct 06 '22 20:10

Jeff Pal