无法验证第一个证书
证书链不完整。
这意味着您正在连接的Web服务器配置有误,并未在发送给您的证书链中包含中间证书。
证书链
它最可能如下所示:
- 服务器证书 - 存储由中间证书签名的证书。
- 中间证书 - 存储由根证书签名的证书。
- 根证书 - 存储自签名证书。
中间证书应与服务器证书一起安装在服务器上。
根证书嵌入到软件应用程序、浏览器和操作系统中。
颁发证书的应用程序必须发送完整的证书链,这意味着服务器证书本身和所有中间证书。客户端应该知道根证书。
重现问题
使用浏览器前往https://incomplete-chain.badssl.com。
它不会显示任何错误(地址栏中的挂锁是绿色的)。
这是因为浏览器倾向于补全证书链,如果服务器没有发送,则会自动补全。
现在,使用Node连接到https://incomplete-chain.badssl.com:
const axios = require('axios');
axios.get('https://incomplete-chain.badssl.com')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
日志: "Error: unable to verify the first certificate".
解决方案
您需要自己完成证书链。
方法如下:
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_CA_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内置的根证书颁发机构。
这就是为什么我们需要创建自己的根证书颁发机构。使用ssl-root-cas。
然后,创建一个自定义的https
代理,并配置我们的证书包(根证书和中间证书)。在进行请求时,将此代理传递给axios
。
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
资源:
- 如何解决 Node.js 应用在涉及 SSL 调用时出现证书错误
- ssl-root-cas 包
- Node.js 问题:Unable to Verify the First Certificate
- 如何检查 CA 链的安装
- 如何将远程服务器的 SSL 证书保存为本地文件
- 如何将 .crt 文件转换为 .pem 格式