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

Posted

技术标签:

【中文标题】错误:无法验证nodejs中的第一个证书【英文标题】:Error: unable to verify the first certificate in nodejs 【发布时间】:2015-10-18 20:21:42 【问题描述】:

我正在尝试使用 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);
      
    );
);

【问题讨论】:

你能解决这个问题吗? 我使用了另一个程序,例如禁用证书验证并完成 你能详细说明一下吗?这对我真的很有帮助 请参阅下面的答案以验证我们需要拒绝未授权的证书 【参考方案1】:

尝试添加适当的根证书

这总是比盲目接受未经授权的端点更安全的选择,而后者只能作为最后的手段。

这可以像添加一样简单

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

到您的应用程序。

SSL Root CAs npm package(在此处使用)是解决此问题的非常有用的软件包。

【讨论】:

这个答案应该在大多数情况下使用,因为它实际上解决了问题,而不是禁用 SSL 的全部好处。 如 ssl-root-cas 模块自述文件中所述,导致此问题的最常见原因之一是您的证书未嵌入其中间 CA 证书。在尝试其他任何事情之前先尝试修复您的证书;) mkcert 不会创建“全链”证书。您必须将您的证书与新证书文件中$(mkcert -CAROOT)/rootCA.pem 提供的根证书连接起来,并执行类似https.globalAgent.options.ca = fs.readFileSync('fullchain.pem') 的操作,请参阅github.com/FiloSottile/mkcert/issues/76 出于安全考虑,ssl-root-cas npm 模块向 mozilla.org 发出硬编码 git.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/… 的请求。它可能是安全的,因为 Mozilla 但它似乎是一个攻击媒介。 这对我不起作用,但这个对我有用:github.com/arvind-agarwal/node_extra_ca_certs_mozilla_bundle【参考方案2】:

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

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0

【讨论】:

这似乎与Labeo's answer above 没有什么不同,同样危险。 不同的是,它不需要任何编码更改,因为 env 变量可以在源代码之外设置。 这个答案很危险。您正在禁用 TLS 提供的任何安全性。 这对我有用,非常有帮助。就我而言,我只是在与 localhost 交谈,所以安全不是问题。 确实只是为了测试本地主机。只需确保在测试后将其删除即可。【参考方案3】:

unable to verify the first certificate

证书链不完整。

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

证书链

它很可能如下所示:

    服务器证书 - 存储由中间人签署的证书。 中间证书 - 存储由 root 签名的证书。 根证书 - 存储自签名证书。

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

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

重现问题

使用浏览器访问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_EXTRA_CA_CERTS扩展Node的内置证书存储,

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

1。如何获得中级证书?

使用openssl(附带Git for Windows)。

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

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

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

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

2b。 ca 选项

此选项将覆盖 Node 的内置根 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 全局代理上,而不是创建自定义https 代理并将其传递给axios

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

资源:

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

【讨论】:

更好的解释和解决方案,不可能,谢谢!【参考方案4】:

因为无法验证 nodejs 中的第一个证书,所以需要拒绝未经授权

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

【讨论】:

这个答案很危险。另一个更安全。 那么这样做,您就消除了 SSL 提供的安全性,所以它应该只用于开发。 不检查证书意味着您无法确定对方的身份,因此可能会受到欺骗主机的影响。但是,即使您不检查证书,您仍然会获得无法(轻松)监视的加密通信。因此,添加这一行并不会“消除 SSL 的安全性”,也不会像另一位评论者所说的“禁用 [] SSL 的全部好处”。 禁用 SSL 验证不能解决任何问题。:-) 如果您使用的是节点请求库,则此方法有效。我是谁。谢谢你,它解决了我迫切的开发需求。【参考方案5】:

您尝试下载的服务器可能配置错误。即使它在您的浏览器中工作,它也可能不会在链中包含缓存空客户端验证所需的所有公共证书。

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

查找此错误:

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

还有这个:

链问题............不完整

【讨论】:

我从 DigiCert Inc. 授权的证书中遇到此问题(链问题.........不完整),解决此问题的程序是什么? @imarchuang 简而言之,您的服务器不仅需要为您的域提供证书,还需要提供中间证书。我无法在此评论中提供更多详细信息,但希望这些信息足以为您指明正确的方向。 非常感谢,我们也通过梳理根证书想通了 谢谢!我发现我的证书不完整,虽然它在 chrome 和 firefox 中运行良好,但在电子应用程序中不起作用,我通过 cat domainname.crt domainname.ca-bundle &gt; domainname-ssl-bundle.crt 将它修复在 nginx【参考方案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
);

【讨论】:

恕我直言,这是最好的解决方案,因为它不需要额外的库并且很简单【参考方案7】:

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

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

【讨论】:

这解决了我的问题,我使用的是 'request' 模块而不是 'http' 。谢谢!【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

GoDaddy SSL 证书

我在尝试使用 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

【讨论】:

这对我有用,只是在导入时我必须使用“ssl-root-cas”而不是“ssl-root-cas/latest”。【参考方案10】:

您可以全局禁用证书检查 - 无论您使用哪个软件包来发出请求 - 就像这样:

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

或者

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

当然你不应该这样做 - 但它对于调试和/或非常基本的脚本编写肯定很方便,你绝对不关心证书是否正确验证。

【讨论】:

【参考方案11】:

在开发环境中设置:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

或者,先设置环境变量

export NODE_TLS_REJECT_UNAUTHORIZED=0   

然后启动应用程序:

node index.js

不适合产品服务。

【讨论】:

对于本地主机开发,这是可行的解决方案。【参考方案12】:

这对我有用 => 添加代理并将“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;
    );
;

【讨论】:

关于安全性的重要一点是不要删除安全性...【参考方案13】:

几天前我遇到了这个问题,这是我遵循的方法,它对我有用。

对我来说,当我在公司防火墙下尝试使用 axios 获取数据或获取库时,就会发生这种情况,因此我们有某些特定的证书,节点 js 证书存储无法指向这些证书。

所以对于我的 loclahost,我采用了这种方法。 我在我的项目中创建了一个文件夹,并将整个证书链保存在文件夹和我的 dev-server(package.json) 脚本中,我将它与服务器脚本一起添加,以便节点 js 可以引用路径。

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

对于我的服务器(不同的环境),我创建了一个如下的新环境变量并添加了它。我使用的是 Openshift,但我想其他人的概念也是一样的。

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

我没有生成任何证书,因为我已经可以使用整个证书链。

【讨论】:

It's also important that this environment variable is set before Node is started。否则会被忽略。因此,例如,NODE_EXTRA_CA_CERTS=certificates/certs-bundle.crt 将不起作用,也不会使用类似 dotenv npm 包的东西。【参考方案14】:

我遇到了非常罕见的情况,但希望它可以对某人有所帮助:制作了一个代理服务,它将请求代理到另一个服务。即使我添加了所有预期的证书,每个请求的错误都是“无法验证第一个证书”。

原因很简单——我不小心重新发送了“主机”标头。 只要确保您没有明确发送“主机”标头即可。

【讨论】:

【参考方案15】:

我能够通过 mozilla 或 chrome 等浏览器获取证书链。

    打开网站,进入网页的证书设置,下载证书链为文件名(first-chain.pem,second-chain.pem),应该是pem格式
----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
......
-----END CERTIFICATE-----
----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
......
-----END CERTIFICATE-----
    然后在您的 nodejs 代码中,我是在 typescript 上完成的,我添加了 2 个 cas,因为我有 2 个网络服务器请求
import https from 'https'
import cas from 'ssl-root-cas'

......

 interface CaList extends Buffer 
  addFile(file: string): Buffer[]
 
 const caList = cas.create() as CaList
 caList.addFile(process.env.PROJECT_PATH + 'certs/first-chain.pem')
 caList.addFile(process.env.PROJECT_PATH + 'certs/second-chain.pem')

然后由于我需要建立 websocket wss 连接,我将带有新 cas 列表的代理添加到请求中

this.client.connect(KtUrl, undefined, undefined, undefined, 
    agent: new https.Agent(
      ca: caList
    )
)

还必须为 ssl-root-cas 文件名 ssl-root-cas.d.ts 添加定义文件,以便打字稿不会抱怨

declare module 'ssl-root-cas' 
  function create(): string | Buffer | (string | Buffer)[] | undefined

【讨论】:

【参考方案16】:

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

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

【讨论】:

以上是关于错误:无法验证nodejs中的第一个证书的主要内容,如果未能解决你的问题,请参考以下文章

OpenSSL:无法验证 Experian URL 的第一个证书

错误:设置 id_rsa 后无法验证第一个证书

Node-RED 和 nodemailer - 错误:无法验证第一个证书

SwagQL - 无法验证第一个证书

NodeJS:以 DER 格式验证证书

“验证返回码:21(无法验证第一个证书)”