Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: unable to verify the first certificate in nodejs

People also ask

How do I fix unable to verify the first certificate?

To fix 'Error: unable to verify the first certificate' in Node. js, we should set the appropriate root certificate. require('https'). globalAgent.options.ca = require('ssl-root-cas/latest').

How do I fix unable to verify the first certificate in Postman?

First, your OS (Windows, Mac, Linux) must trust this certificate. Then, in Postman, go to Settings > Certificates, and enable CA certification, then select the same trusted certificate.

What is Node_extra_ca_certs?

NODE_EXTRA_CA_CERTS. From Node version 7.3. 0, NODE_EXTRA_CA_CERTS environment variable is introduced to pass in a CA certificate file. This allows the “root” CAs to be extended with the extra certificates in file. The file should consist of one or more trusted certificates in PEM format.


Try adding the appropriate root certificate

This is always going to be a much safer option than just blindly accepting unauthorised end points, which should in turn only be used as a last resort.

This can be as simple as adding

require('https').globalAgent.options.ca = require('ssl-root-cas/latest').create();

to your application.

The SSL Root CAs npm package (as used here) is a very useful package regarding this problem.


unable to verify the first certificate

The certificate chain is incomplete.

It means that the webserver you are connecting to is misconfigured and did not include the intermediate certificate in the certificate chain it sent to you.

Certificate chain

It most likely looks as follows:

  1. Server certificate - stores a certificate signed by intermediate.
  2. Intermediate certificate - stores a certificate signed by root.
  3. Root certificate - stores a self-signed certificate.

Intermediate certificate should be installed on the server, along with the server certificate.
Root certificates are embedded into the software applications, browsers and operating systems.

The application serving the certificate has to send the complete chain, this means the server certificate itself and all the intermediates. The root certificate is supposed to be known by the client.

Recreate the problem

Go to https://incomplete-chain.badssl.com using your browser.

It doesn't show any error (padlock in the address bar is green).
It's because browsers tend to complete the chain if it’s not sent from the server.

Now, connect to https://incomplete-chain.badssl.com using Node:

// index.js
const axios = require('axios');

axios.get('https://incomplete-chain.badssl.com')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Logs: "Error: unable to verify the first certificate".

Solution

You need to complete the certificate chain yourself.

To do that:

1: You need to get the missing intermediate certificate in .pem format, then

2a: extend Node’s built-in certificate store using NODE_EXTRA_CA_CERTS,

2b: or pass your own certificate bundle (intermediates and root) using ca option.

1. How do I get intermediate certificate?

Using openssl (comes with Git for Windows).

Save the remote server's certificate details:

openssl s_client -connect incomplete-chain.badssl.com:443 -servername incomplete-chain.badssl.com | tee logcertfile

We're looking for the issuer (the intermediate certificate is the issuer / signer of the server certificate):

openssl x509 -in logcertfile -noout -text | grep -i "issuer"

It should give you URI of the signing certificate. Download it:

curl --output intermediate.crt http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt

Finally, convert it to .pem:

openssl x509 -inform DER -in intermediate.crt -out intermediate.pem -text

2a. NODE_EXTRA_CERTS

I'm using cross-env to set environment variables in package.json file:

"start": "cross-env NODE_EXTRA_CA_CERTS=\"C:\\Users\\USERNAME\\Desktop\\ssl-connect\\intermediate.pem\" node index.js"

2b. ca option

This option is going to overwrite the Node's built-in root CAs.

That's why we need to create our own root CA. Use ssl-root-cas.

Then, create a custom https agent configured with our certificate bundle (root and intermediate). Pass this agent to axios when making request.

// index.js
const axios = require('axios');
const path = require('path');
const https = require('https');
const rootCas = require('ssl-root-cas').create();

rootCas.addFile(path.resolve(__dirname, 'intermediate.pem'));
const httpsAgent = new https.Agent({ca: rootCas});

axios.get('https://incomplete-chain.badssl.com', { httpsAgent })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Instead of creating a custom https agent and passing it to axios, you can place the certifcates on the https global agent:

// Applies to ALL requests (whether using https directly or the request module)
https.globalAgent.options.ca = rootCas;

Resources:

  1. https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded
  2. https://www.npmjs.com/package/ssl-root-cas
  3. https://github.com/nodejs/node/issues/16336
  4. https://www.namecheap.com/support/knowledgebase/article.aspx/9605/69/how-to-check-ca-chain-installation
  5. https://superuser.com/questions/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file/
  6. How to convert .crt to .pem

Another dirty hack, which will make all your requests insecure:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0

for unable to verify the first certificate in nodejs reject unauthorized is needed

 request({method: "GET", 
        "rejectUnauthorized": false, 
        "url": url,
        "headers" : {"Content-Type": "application/json",
        function(err,data,body) {
    }).pipe(
       fs.createWriteStream('file.html'));

The server you're trying to download from may be badly configured. Even if it works in your browser, it may not be including all the public certificates in the chain needed for a cache-empty client to verify.

I recommend checking the site in SSLlabs tool: https://www.ssllabs.com/ssltest/

Look for this error:

This server's certificate chain is incomplete.

And this:

Chain issues.........Incomplete