错误:无法验证Node.js中的第一个证书


141

我正在尝试使用URL从jira服务器下载文件,但出现错误。如何在代码中包括证书以验证 错误:

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)

我的Nodejs代码:

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

    });
});

你能解决这个问题吗?
sharad jain 2015年

1
我使用了另一个过程,例如禁用证书验证并完成了
Labeo,2015年

你能再详细一点吗?这对我真的很有帮助
sharad jain 2015年

请参阅下面的答案以验证我们是否需要拒绝证书的有效性未授权
Labeo,

Answers:


120

尝试添加适当的根证书

这总是比盲目地接受未经授权的端点要安全得多的选择,后者只能被用作最后的手段。

这可以像添加一样简单

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

到您的应用程序。

SSL根CA NPM包(这里使用的)是关于这个问题的一个非常有用的包。


9
在大多数情况下,应使用此答案,因为它实际上可以解决问题,而不是禁用SSL的全部好处。
mikemaccana

12
如ssl-root-cas模块README中所述,此问题的最常见原因之一是您的证书未嵌入其中间CA证书。在尝试其他任何操作之前,请先尝试修正证书;)
Laurent VB

您甚至可能不需要SSL-root-cas软件包。只需将globalAgents.option.cert设置为全链证书即可。那就是解决我的问题的原因。
smartexpert

1
mkcert不会创建“全链”证书。您必须将证书与$(mkcert -CAROOT)/rootCA.pem位于新证书文件中的根证书连接起来,并执行类似的操作,https.globalAgent.options.ca = fs.readFileSync('fullchain.pem')请参阅github.com/FiloSottile/mkcert/issues/76
Frosty Z

为了安全起见,ssl-root-casnpm模块有一个请求,要求发送到mozilla.org硬编码的git.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/…。这可能是安全的,因为Mozilla似乎是一种攻击媒介。
Avindra Goolcharan

60

另一个肮脏的hack,这会使您的所有请求都不安全:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0

8
同样危险,这似乎与Labeo的回答没有什么不同。
ocramot

4
有所不同,它不需要任何编码更改,因为可以在源代码外部设置env变量。
jzacharuk

1
这个答案很危险。您正在禁用TLS提供的任何安全性。
Flimm

1
这对我有用,超级有帮助。就我而言,我只是在与localhost交谈,因此安全性不是问题。
Mike S

确实确实很好,只是为了测试本地主机。只需确保在测试后将其删除即可。
Nico

44

对于无法验证nodejs中的第一个证书,需要拒绝未授权

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

129
这个答案很危险。另一个更安全。
mikemaccana '16

3
这样做可以消除SSL提供的安全性,因此它仅应用于开发。
西尔万

11
不检查证书意味着您无法确定另一方的身份,因此可能受到欺骗主机的影响。但是,即使您不检查证书,也仍然会获得无法(轻松)监视的加密通信。因此,添加此行不会“删除SSL的安全性”,也不会像另一位评论者所说的那样“禁用SSL的全部好处”。
鲍勃·波拉克

4
禁用SSL验证不是解决任何问题的方法。:-)
Siddhu,2016年

9
如果您正在使用节点请求库,则此方法有效。我是。谢谢您,它解决了我眼中的开发需求。
艾伦(Alan)

29

您尝试从中下载的服务器可能配置错误。即使它在您的浏览器中可以使用,它也可能未包含清空缓存的客户端进行验证所需的所有公共证书。

我建议在SSLlabs工具中检查网站:https ://www.ssllabs.com/ssltest/

查找此错误:

该服务器的证书链不完整。

还有这个:

连锁问题.........不完整


我得到了DigiCert Inc.授权的证书的此问题(链接问题.........不完整),如何解决此问题?
imarchuang

@imarchuang简而言之,您的服务器不仅需要提供域的证书,还需要提供中间证书。我无法在此评论中提供更多细节,但希望有足够的信息为您指明正确的方向。
Flimm

非常感谢,我们也通过梳理根证书来解决问题
imarchuang

谢谢!我发现我的证书不完整,尽管它在chrome和firefox中可以正常使用,但在电子应用程序中却不起作用,我将它固定在了nginx上cat domainname.crt domainname.ca-bundle > domainname-ssl-bundle.crt
Ivan Borshchov

25

unable to verify the first certificate

证书链不完整。

这意味着您要连接的Web服务器配置错误,并且在发送给您的证书链中未包含中间证书。

证书链

它最可能如下所示:

  1. 服务器证书-存储由中间人签名的证书。
  2. 中间证书-存储由root签名的证书。
  3. 根证书-存储自签名证书。

中间证书应与服务器证书一起安装在服务器上。
根证书已嵌入到软件应用程序,浏览器和操作系统中。

提供证书的应用程序必须发送完整的链,这意味着服务器证书本身以及所有中间产品。客户端应该知道根证书。

重现问题

使用浏览器访问https://incomplete-chain.badssl.com

它没有显示任何错误(地址栏中的挂锁为绿色)。
这是因为如果不是从服务器发送的,浏览器往往会完成该链

现在,使用Node 连接到https://incomplete-chain.badssl.com

// 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);
  });

记录:“ 错误:无法验证第一个证书 ”。

您需要自己完成证书链。

要做到这一点:

1:您需要以.pem格式获取丢失的中间证书,然后

2a:使用扩展Node的内置证书存储NODE_EXTRA_CA_CERTS

2b:或使用ca选项传递您自己的证书捆绑包(中间件和根)。

1.如何获得中间证书?

使用openssl适用于Windows的Git附带)。

保存远程服务器的证书详细信息:

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

我们正在寻找颁发者(中间证书是服务器证书的颁发者/签名者):

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

它应该给您签名证书的URI。下载它:

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

最后,将其转换为.pem

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

2a。NODE_EXTRA_CERTS

我正在使用交叉环境在package.json文件中设置环境变量:

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

2b。ca选项

此选项将覆盖节点的内置根CA。

这就是为什么我们需要创建自己的根CA。使用ssl-root-cas

然后,创建一个https使用我们的证书捆绑包配置的自定义代理(根证书和中间证书)。axios发出请求时,将此代理传递给。

// 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);
  });

无需创建自定义https代理并将其传递给axios,您可以将证书放在https全局代理上:

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

资源:

  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. /superuser/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file/
  6. 如何将.crt转换为.pem

非常详细的解释。
7

非常精彩!没有为我工作,但有什么细节!
汤姆·查达拉维修斯

6

这实际上从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
});

1
那是最好的解决方案,恕我直言,因为它不需要其他库并且很简单
Martin Schneider

4

您可以通过如下修改请求选项来做到这一点。如果您使用的是自签名证书或缺少中介,则将strictSSL设置为false不会强制请求程序包验证证书。

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

这解决了我的问题,我使用的是'request'模块而不是'http'。谢谢!
Bruno Nunes

2

GoDaddy SSL CC证书

我在尝试使用GoDaddy证书连接到我们的后端API服务器时遇到了这种情况,这是我用来解决问题的代码。

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:

使用捆绑的证书,不要忘记安装库 npm install ssl-root-cas


1
这对我有用,除了在导入时我必须使用“ ssl-root-cas”而不是“ ssl-root-cas / latest”。
krishnan

2

这对我有用=>添加代理并将'rejectUnauthorized'设置为false

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


1

解决此问题的另一种方法是使用以下模块。

node_extra_ca_certs_mozilla_bundle

通过生成包含Mozilla信任的所有根证书和中间证书的PEM文件,该模块可以在不进行任何代码修改的情况下工作。您可以使用以下环境变量(适用于Nodejs v7.3 +),

NODE_EXTRA_CA_CERTS

生成要与上述环境变量一起使用的PEM文件。您可以使用以下方法安装模块:

npm install --save node_extra_ca_certs_mozilla_bundle

然后使用环境变量启动节点脚本。

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

使用生成的PEM文件的其他方法可在以下位置找到:

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

注意:我是上述模块的作者。


-3

我正在使用nodemailer npm模块。下面的代码解决了这个问题

     tls: {
     // do not fail on invalid certs
     rejectUnauthorized: false
     }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.