错误:无法验证nodejs中的第一个证书

Lab*_*beo 105 jira ssl-certificate node.js

我正在尝试使用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)
Run Code Online (Sandbox Code Playgroud)

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

    });
});
Run Code Online (Sandbox Code Playgroud)

小智 103

unable to verify the first certificate

证书链不完整。

这意味着您连接的网络服务器配置错误,并且在它发送给您的证书链中没有包含中间证书。

证书链

它很可能如下所示:

  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);
  });
Run Code Online (Sandbox Code Playgroud)

日志:“错误:无法验证第一个证书”。

解决方案

您需要自己完成证书链。

要做到这一点:

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

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

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

1.如何获得中级证书?

使用openssl(附带Git for Windows)。

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

openssl s_client -connect incomplete-chain.badssl.com:443 -servername incomplete-chain.badssl.com | tee logcertfile
Run Code Online (Sandbox Code Playgroud)

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

openssl x509 -in logcertfile -noout -text | grep -i "issuer"
Run Code Online (Sandbox Code Playgroud)

它应该为您提供签名证书的 URI。下载它:

curl --output intermediate.crt http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
Run Code Online (Sandbox Code Playgroud)

最后,将其转换为.pem

openssl x509 -inform DER -in intermediate.crt -out intermediate.pem -text
Run Code Online (Sandbox Code Playgroud)

2a. NODE_EXTRA_CERTS

我正在使用cross-envpackage.json文件中设置环境变量:

"start": "cross-env NODE_EXTRA_CA_CERTS=\"C:\\Users\\USERNAME\\Desktop\\ssl-connect\\intermediate.pem\" node index.js"
Run Code Online (Sandbox Code Playgroud)

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);
  });
Run Code Online (Sandbox Code Playgroud)

您可以将证书放在全局代理上,而不是创建自定义https代理并将其传递给:axioshttps

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

资源:

  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. 如何将 .crt 转换为 .pem


Jos*_*hua 99

尝试添加适当的根证书

这总是比盲目接受未经授权的终点更安全的选择,而这些终点反过来只能作为最后的手段使用.

这可以像添加一样简单

require('https').globalAgent.options.ca = require('ssl-root-cas/latest').create();
Run Code Online (Sandbox Code Playgroud)

到您的申请.

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

  • 如ssl-root-cas模块README中所述,此问题的最常见原因之一是您的证书未嵌入其中间CA证书.在尝试其他任何事情之前尝试修复证书;) (9认同)
  • 在大多数情况下应该使用此答案,因为它实际上解决了问题,而不是禁用SSL的全部优势. (7认同)
  • **mkcert** 不会创建“全链”证书。您必须将您的证书与新证书文件中`$(mkcert -CAROOT)/rootCA.pem` 提供的根证书连接起来,然后执行类似`https.globalAgent.options.ca = fs.readFileSync('fullchain.pem ')` 见 https://github.com/FiloSottile/mkcert/issues/76 (4认同)

小智 41

另一个肮脏的黑客,这将使你的所有请求不安全:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
Run Code Online (Sandbox Code Playgroud)

  • 这似乎与[Labeo的上述回答](/sf/answers/2258804831/)一样,但同样危险。 (6认同)
  • 确实很好,只是为了测试本地主机。只需确保在测试后将其删除即可。 (5认同)
  • 有所不同,它不需要任何编码更改,因为可以在源代码外部设置env变量。 (4认同)
  • 这对我有用,超级有帮助。就我而言,我只是与 https://localhost 通信,因此安全性不是问题。 (4认同)
  • 这个答案很危险。您将禁用 TLS 提供的任何安全性。 (2认同)

Lab*_*beo 33

如果无法验证nodejs中的第一个证书,则需要拒绝未经授权

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

  • **这个答案很危险.**另一个更安全. (114认同)
  • 不检查证书意味着您无法确定另一方的身份,因此可能会受到欺骗主机的约束.但是,即使您不检查证书,仍然可以获得无法(轻松)监视的加密通信.因此,添加此行不会"删除SSL"的安全性,因为另一位评论者说"禁用[] SSL的全部好处". (11认同)
  • 如果您使用节点请求库,则此方法有效.我是谁 谢谢你,它解决了我对开发的迫切需要. (9认同)
  • @Voo,它适用于开发环境. (6认同)
  • 禁用SSL验证不是任何问题的解决方案.:-) (4认同)
  • 通过这样做,您删除了SSL提供的安全性,因此它应该仅用于开发. (3认同)
  • OP的代码在哪里? (2认同)
  • @SetWhite 让开发、测试和生产以如此重要的方式存在差异也是一个糟糕的主意(您在那里禁用了大量错误检查)-只会让您的生活更艰难,您的测试更糟。而且我不想知道有多少开发人员在他们的代码库中禁用了检查并且从来没有解决潜在的问题并因此发送不安全、损坏的代码。 (2认同)
  • 确实是危险的,但在安全的情况下,这是一种无痛的解决方案。 (2认同)

Fli*_*imm 25

您尝试从中下载的服务器可能配置不当.即使它在您的浏览器中有效,它也可能不包括缓存空客户端验证所需的链中的所有公共证书.

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

寻找这个错误:

此服务器的证书链不完整.

还有这个:

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


Pin*_*Woo 15

在开发环境中设置:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
Run Code Online (Sandbox Code Playgroud)

或者,先设置环境变量

export NODE_TLS_REJECT_UNAUTHORIZED=0   
Run Code Online (Sandbox Code Playgroud)

然后启动应用程序:

node index.js
Run Code Online (Sandbox Code Playgroud)

不适合产品服务。

  • 对于本地主机开发,这是可行的解决方案。 (5认同)

arv*_*rva 9

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

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
Run Code Online (Sandbox Code Playgroud)

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

NODE_EXTRA_CA_CERTS=node_modules/node_extra_ca_certs_mozilla_bundle/ca_bundle/ca_intermediate_root_bundle.pem node your_script.js
Run Code Online (Sandbox Code Playgroud)

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

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

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


koo*_*ang 8

这实际上为我解决了这个问题,来自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
});
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,这是最好的解决方案,因为它不需要额外的库并且很简单 (2认同)

小智 6

您可以通过修改请求选项来执行此操作,如下所示.如果您使用自签名证书或缺少中介,则将strictSSL设置为false将不会强制请求程序包验证证书.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}
Run Code Online (Sandbox Code Playgroud)