[node.js] Error: unable to verify the first certificate in nodejs

I'm trying to download a file from jira server using an url but I'm getting an error. how to include certificate in the code to verify Error:

Error: unable to verify the first certificate in nodejs

at Error (native)
    at TLSSocket.<anonymous> (_tls_wrap.js:929:36)

  at TLSSocket.emit (events.js:104:17)

at TLSSocket._finishInit (_tls_wrap.js:460:8)

My Nodejs code:

var https = require("https");
var fs = require('fs');
var options = {
    host: 'jira.example.com',
    path: '/secure/attachment/206906/update.xlsx'
};

https.get(options, function (http_res) {

    var data = "";


    http_res.on("data", function (chunk) {

        data += chunk;
    });


    http_res.on("end", function () {

        var file = fs.createWriteStream("file.xlsx");
        data.pipe(file);

    });
});

This question is related to node.js ssl-certificate jira

The answer is


I met very rare case, but hopely it could help to someone: made a proxy service, which proxied requests to another service. And every request's error was "unable to verify the first certificate" even when i added all expected certificates.

The reason was pretty simple - i accidently re-sent also the "host" header. Just make sure you don't send "host" header explicitly.


You may be able to do this by modifying the request options as below. If you are using a self-signed certificate or a missing intermediary, setting strictSSL to false will not force request package to validate the certificate.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}

You can disable certificate checking globally - no matter which package you are using for making requests - like this:

// Disable certificate errors globally
// (ES6 imports (eg typescript))
//
import * as https from 'https'
https.globalAgent.options.rejectUnauthorized = false

Or

// Disable certificate errors globally
// (vanilla nodejs)
//
require('https').globalAgent.options.rejectUnauthorized = false

Of course you shouldn't do this - but it's certainly handy for debugging and/or very basic scripting where you absolutely don't care about certificates being validated correctly.


Another approach to solve this is to use the following module.

node_extra_ca_certs_mozilla_bundle

This module can work without any code modification by generating a PEM file that includes all root and intermediate certificates trusted by Mozilla. You can use the following environment variable (Works with Nodejs v7.3+),

NODE_EXTRA_CA_CERTS

To generate the PEM file to use with the above environment variable. You can install the module using:

npm install --save node_extra_ca_certs_mozilla_bundle

and then launch your node script with an environment variable.

NODE_EXTRA_CA_CERTS=node_modules/node_extra_ca_certs_mozilla_bundle/ca_bundle/ca_intermediate_root_bundle.pem node your_script.js

Other ways to use the generated PEM file are available at:

https://github.com/arvind-agarwal/node_extra_ca_certs_mozilla_bundle

NOTE: I am the author of the above module.


I was using nodemailer npm module. The below code solved the issue

     tls: {
     // do not fail on invalid certs
     rejectUnauthorized: false
     }

This actually solved it for me, from https://www.npmjs.com/package/ssl-root-cas

// INCORRECT (but might still work)
var server = https.createServer({
  key: fs.readFileSync('privkey.pem', 'ascii'),
  cert: fs.readFileSync('cert.pem', 'ascii') // a PEM containing ONLY the SERVER certificate
});

// CORRECT (should always work)
var server = https.createServer({
  key: fs.readFileSync('privkey.pem', 'ascii'),
  cert: fs.readFileSync('fullchain.pem', 'ascii') // a PEM containing the SERVER and ALL INTERMEDIATES
});

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

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0

This Worked For me => adding agent and 'rejectUnauthorized' set to false

_x000D_
_x000D_
const https = require('https'); //Add This_x000D_
const bindingGridData = async () => {_x000D_
  const url = `your URL-Here`;_x000D_
  const request = new Request(url, {_x000D_
    method: 'GET',_x000D_
    headers: new Headers({_x000D_
      Authorization: `Your Token If Any`,_x000D_
      'Content-Type': 'application/json',_x000D_
    }),_x000D_
    //Add The Below_x000D_
    agent: new https.Agent({_x000D_
      rejectUnauthorized: false,_x000D_
    }),_x000D_
  });_x000D_
  return await fetch(request)_x000D_
    .then((response: any) => {_x000D_
      return response.json();_x000D_
    })_x000D_
    .then((response: any) => {_x000D_
      console.log('response is', response);_x000D_
      return response;_x000D_
    })_x000D_
    .catch((err: any) => {_x000D_
      console.log('This is Error', err);_x000D_
      return;_x000D_
    });_x000D_
};
_x000D_
_x000D_
_x000D_


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

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'));

GoDaddy SSL CCertificate

I've experienced this while trying to connect to our backend API server with GoDaddy certificate and here is the code that I used to solve the problem.

var rootCas = require('ssl-root-cas/latest').create();

rootCas
  .addFile(path.join(__dirname, '../config/ssl/gd_bundle-g2-g1.crt'))
  ;

// will work with all https requests will all libraries (i.e. request.js)
require('https').globalAgent.options.ca = rootCas;

PS:

Use the bundled certificate and don't forget to install the library npm install ssl-root-cas


I faced this issue few days back and this is the approach I followed and it works for me.

For me this was happening when i was trying to fetch data using axios or fetch libraries as i am under a corporate firewall, so we had certain particular certificates which node js certificate store was not able to point to.

So for my loclahost i followed this approach. I created a folder in my project and kept the entire chain of certificates in the folder and in my scripts for dev-server(package.json) i added this alongwith server script so that node js can reference the path.

"dev-server":set NODE_EXTRA_CA_CERTS=certificates/certs-bundle.crt

For my servers(different environments),I created a new environment variable as below and added it.I was using Openshift,but i suppose the concept will be same for others as well.

"name":NODE_EXTRA_CA_CERTS
"value":certificates/certs-bundle.crt

I didn't generate any certificate in my case as the entire chain of certificates was already available for me.


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


Examples related to node.js

Hide Signs that Meteor.js was Used Querying date field in MongoDB with Mongoose SyntaxError: Cannot use import statement outside a module Server Discovery And Monitoring engine is deprecated How to fix ReferenceError: primordials is not defined in node UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac internal/modules/cjs/loader.js:582 throw err DeprecationWarning: Buffer() is deprecated due to security and usability issues when I move my script to another server Please run `npm cache clean`

Examples related to ssl-certificate

How to install OpenSSL in windows 10? Scraping: SSL: CERTIFICATE_VERIFY_FAILED error for http://en.wikipedia.org Not able to install Python packages [SSL: TLSV1_ALERT_PROTOCOL_VERSION] Letsencrypt add domain to existing certificate javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure bypass invalid SSL certificate in .net core How to add Certificate Authority file in CentOS 7 How to use a client certificate to authenticate and authorize in a Web API This certificate has an invalid issuer Apple Push Services iOS9 getting error “an SSL error has occurred and a secure connection to the server cannot be made”

Examples related to jira

Error: unable to verify the first certificate in nodejs JIRA JQL searching by date - is there a way of getting Today() (Date) instead of Now() (DateTime)