为什么 requestjs 拒绝适用于 Firefox 的自签名 SSL 证书?

Why does requestjs reject a self-signed SSL certificate that works with Firefox?

情况是这样的。我创建了一个自签名 CA 证书,并用它来签署第二个用于 https 的证书。 Web 服务器是 nginx,对 expressjs 应用程序执行 SSL 终止和反向代理。为了验证信任链是否正确,我在 Firefox 中安装了 CA,并且能够通过 https 访问该网站而没有警告,正如预期的那样。此外,我可以使用 openssl x509 -in server.crt -text -noout 检查服务器的证书,并且我看到了预期的颁发者,特别是主题的预期通用名称。 (注意:此处使用的通用名称是IP地址,以免造成麻烦。)

但是,当我尝试使用 requestjs 通过 nodejs 脚本访问服务器时,事情并没有那么顺利。在脚本中,使用如下代码加载 CA 证书:

request.get({url: theUrl, ca: fs.readFileSync("./ca.crt")}, ...

但是我得到了这个错误(为了便于阅读,换行,原来是一行):

Contacting doorman failed: Error: Hostname/IP doesn't match certificate's altnames: 
"IP: <redacted> is not in the cert's list: "

特别可疑的是,好像在说"cert's list"是空的。在其他答案中建议在选项中使用 rejectUnauthorized: false,但对于此应用程序来说这不是一个很好的选择,因为我想要进行身份验证。

如何让requestjs/nodejs信任这个证书?


服务器证书的内容,由 openssl x509 -text

报告
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 3 (0x3)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=State, L=City, O=Company, CN=www.company.com
        Validity
            Not Before: Dec  7 17:19:51 2015 GMT
            Not After : Oct 14 17:19:51 2025 GMT
        Subject: C=US, ST=State, L=City, O=Company, CN=1.2.3.4/emailAddress=webmaster@company.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

以及用于生成该证书的配置文件:

[ req ]
default_bits       = 4096
prompt             = no
encrypt_key        = no

distinguished_name = req_distinguished_name
req_extensions     = v3_req

[ req_distinguished_name ]
countryName            = "US"
stateOrProvinceName    = "State"
localityName           = "City"
organizationName       = "Company"
commonName             = "1.2.3.4"
emailAddress           = "webmaster@company.com"

[ v3_req ]
subjectAltName = IP:1.2.3.4

Hostname/IP doesn't match certificate's altnames:

我的猜测是您的证书包含在 URL 中仅用作主题 (CN) 而不是主题备用名称 (SAN) 的正确主机名,但它包含其他名称作为 SAN。该标准非常明确,如果给出了任何主题替代 DNS 名称,则不应检查 CN,但大多数浏览器无论如何都会检查 CN(我认为 Safari 更严格)。但是 node.js 实施了严格的行为,因此会失败。

如果我的猜测是正确的(没有证书很难看清),那么必须通过创建正确的证书来解决问题。另一种可能性是您在浏览器和 nodejs 中使用略有不同的 URL(例如有无 www 前缀)。

编辑:在看到您实际使用的证书后....

您有一个IP地址在CN的证书。虽然这也被大多数浏览器支持,但这是不对的。 IP 地址必须作为 ipadress SAN 条目而不是 CN 给出。 Nodejs 只希望它在那里,我认为 Safari 也那么严格。请注意,对于 IE,您必须将其放入 CN 或作为 dnsname SAN 条目,因为它们在这种情况下也喜欢违反标准。因此为了安全起见,将其作为 ipaddress、dnsname,也可能作为 CN。