【问题标题】:OpenSSL error - unable to get local issuer certificateOpenSSL 错误 - 无法获取本地颁发者证书
【发布时间】:2018-05-15 02:36:09
【问题描述】:

我有一个简单的链设置,在这种情况下可以成功验证:

$ openssl version
OpenSSL 1.0.2m  2 Nov 2017
$ openssl verify -CAfile chain.pem cert.pem
cert.pem: OK

但是在这些情况下我会收到错误:

$ openssl verify -CAfile ca-cert.pem cert.pem
cert.pem: C = US...
error 2 at 1 depth lookup:unable to get issuer certificate

特别是无法获得颁发者证书

也可以在这里获取:

$ openssl verify chain.pem
chain.pem: C = US...
error 20 at 0 depth lookup:unable to get local issuer certificate

$ openssl verify cert.pem
cert.pem: C...
error 20 at 0 depth lookup:unable to get local issuer certificate

最后,当我将密钥传递给 HTTPS 服务器时,我在 Node.js 中得到它:

events.js:193
      throw er; // Unhandled 'error' event
      ^

Error: unable to get local issuer certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
    at emitNone (events.js:115:13)
    at TLSSocket.emit (events.js:218:7)
    at TLSSocket._finishInit (_tls_wrap.js:637:8)

我尝试使用{ key, cert, ca } 传递它,但仍然是同样的错误。

想知道如何进行调试或解决什么问题才能让 HTTPS 服务器运行。

如果我使用pfx 文件,我会得到以下信息:

events.js:193
      throw er; // Unhandled 'error' event
      ^

Error: self signed certificate in certificate chain
    at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
    at emitNone (events.js:115:13)
    at TLSSocket.emit (events.js:218:7)
    at TLSSocket._finishInit (_tls_wrap.js:637:8)

如果我在 cert 文件中只保留 cert.pem,并将 ca 属性设为 ca-cert.pem,它会给出:

Error: unable to verify the first certificate
    at TLSSocket.<anonymous> (_tls_wrap.js:1108:38)
    at emitNone (events.js:105:13)
    at TLSSocket.emit (events.js:207:7)
    at TLSSocket._finishInit (_tls_wrap.js:638:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:468:38)

不知道该怎么办。

Here 他们说:

OpenSSL 无法找到用于验证签名的颁发者(或在 TLS 握手期间从 Web 服务器接收的链中第一个证书的颁发者)的本地证书。

不知道这是什么意思。

此错误表示证书路径或链已损坏,并且您缺少证书文件。

- https://wiki.zimbra.com/wiki/Fix_depth_lookup:unable_to_get_issuer_certificate

更新

更多帮助:

此问题通常由日志消息指示,例如“无法获取本地颁发者证书”或“自签名证书”。当证书被验证时,它的根 CA 必须被 OpenSSL “信任”,这通常意味着 CA 证书必须放在一个目录或文件中,并且相关程序配置为读取它。 OpenSSL 程序“验证”以类似的方式运行并发出类似的错误消息:查看 verify(1) 程序手册页以获取更多信息。

但仍然没有太大帮助。

看起来 Node.js 使用的是 1.0.2l 而不是 1.0.2m,但似乎没什么大不了的。

$ node -pe process.versions | grep openssl
  openssl: '1.0.2l'

更新 2

奇怪,当我从 Node.js 发出请求时,我得到了这个:

Uncaught Error: unable to verify the first certificate
      at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
      at TLSSocket._finishInit (_tls_wrap.js:637:8)

但是当我去浏览器时,我没有看到“谨慎操作”页面,并且可以在 Node.js 中成功记录一个请求。也许这有点帮助。请帮忙:D

【问题讨论】:

  • 明确一点:cert.pemca-cert.pem 之间有一个中间 CA?而这个中间 CA 在 chain.cert 旁边是 ca-cert.pem
  • ca-cert.pem 上面有一个顶级的自签名证书,所以是的。
  • 回复:“我尝试使用 { key, cert, ca } 传递它,但仍然...”,尝试将 certca 替换为您的 chain.pem 的内容的单个参数(即cert.pem 的内容的串联,后跟ca-cert.pem)。这假定服务器正在使用高于 ca-cert.pem 的根 CA,因为它是受信任的 CA。
  • 更新了问题。奇怪的是,我现在可以在从浏览器访问时让它工作,但不能从 Node.js 访问。

标签: node.js openssl certificate x509certificate


【解决方案1】:

(此答案摘自X509_verify_certcrypto/x509/x509_vfy.c:204,在 openssl-1.0.2m 中)

OpenSSL verify 应用程序以下列方式验证证书:它从目标证书开始构建证书链,并跟踪颁发者链,首先搜索与目标证书一起提供的任何不受信任的证书。在未能找到不受信任的颁发者证书时,OpenSSL 切换到受信任的证书存储并继续构建链。当

  1. 在受信任的商店中找不到发行者。
  2. 遇到自签名证书。
  3. 遇到最大验证深度。

此时,我们的链可能会提前结束(如果我们未能找到发行者,或者如果我们超出了验证深度)。

OpenSSL 然后扫描链上的每个受信任证书,寻找指定受信任证书用途的 SSLv3 扩展。如果受信任的证书具有用于验证操作“目的”的正确“信任”属性(或具有anyExtendedKeyUsage 属性),则该链是受信任的。 (原谅关于信任属性的手波,那部分代码很难阅读。)

所以让我们测试一下。首先,让我们重现 OP 的错误案例:

#
echo "Making Root CA..."
openssl req -newkey rsa:4096 -nodes -keyout ca-key.pem -sha384 -x509 -days 365 -out ca-crt.pem -subj /C=XX/ST=YY/O=RootCA

echo "Making Intermediate CA..."
openssl req -newkey rsa:3072 -nodes -keyout int-key.pem -new -sha384 -out int-csr.pem -subj /C=XX/ST=YY/O=IntermediateCA
openssl x509 -req -days 360 -in int-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out int-crt.pem

echo "Making User Cert..."
openssl req -newkey rsa:2048 -nodes -keyout usr-key.pem -new -sha256 -out usr-csr.pem -subj /C=XX/ST=YY/O=LockCmpXchg8b
openssl x509 -req -days 360 -in usr-csr.pem -CA int-crt.pem -CAkey int-key.pem -CAcreateserial -out usr-crt.pem

echo ""
echo "Making Chain..."
cat ca-crt.pem int-crt.pem > chain.pem

echo ""
echo "Verfying UserCert via RootCA..."
openssl verify -CAfile ca-crt.pem usr-crt.pem

echo ""
echo "Verfying UserCert via IntermediateCA..."
openssl verify -CAfile int-crt.pem usr-crt.pem

echo ""
echo "Verfying UserCert via chain..."
openssl verify -CAfile chain.pem usr-crt.pem

产量

[... Skipping OpenSSL KeyGen / CertGen verbosity ...]
Making Chain...

Verfying UserCert via RootCA...
usr-crt.pem: C = XX, ST = YY, O = LockCmpXchg8b
error 20 at 0 depth lookup:unable to get local issuer certificate

Verfying UserCert via IntermediateCA...
usr-crt.pem: C = XX, ST = YY, O = IntermediateCA
error 2 at 1 depth lookup:unable to get issuer certificate

Verfying UserCert via chain...
usr-crt.pem: OK

现在,让我们使用openssl x509-addtrust 选项来确保我们在中间CA 上具有可接受的信任属性之一(将此称为IntermediateCAWithTrust;我们将使用它来签署AnotherUserCert。 ):

echo ""
echo "Alternate Intermedate CA (using -addtrust anyExtendedKeyUsage)"
echo ""

echo "Making IntermediateCAWithTrust..."
openssl req -newkey rsa:3072 -nodes -keyout int-key2.pem -new -sha384 -out int-csr2.pem -subj /C=XX/ST=YY/O=IntermediateCAWithTrust
openssl x509 -req -days 360 -in int-csr2.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out int-crt2.pem -addtrust anyExtendedKeyUsage

echo "Making AnotherUser Cert..."
openssl req -newkey rsa:2048 -nodes -keyout usr-key2.pem -new -sha256 -out usr-csr2.pem -subj /C=XX/ST=YY/O=LockCmpXchg8b_2
openssl x509 -req -days 360 -in usr-csr2.pem -CA int-crt2.pem -CAkey int-key2.pem -CAcreateserial -out usr-crt2.pem

echo ""
echo "Verfying AnotherUserCert via IntermediateCAWithTrust..."
openssl verify -CAfile int-crt2.pem usr-crt2.pem

这会产生

Alternate Intermedate CA (using -addtrust anyExtendedKeyUsage)

Making IntermediateCAWithTrust...
[... Snip more OpenSSL generation output ...]
Making AnotherUser Cert...
[... Snip more OpenSSL generation output ...]

Verfying AnotherUserCert via IntermediateCAWithTrust...
usr-crt2.pem: OK

嘿看!我们刚刚通过 IntermediateCAWithTrust 成功验证了 AnotherUserCert,即使我们没有提供整个链。这种差异的关键在于链中的任何一个受信任证书都具有适当的信任属性用于验证操作。

再仔细一看 (via openssl x509 -in ca-crt.pem -noout -text),我们的 CA 证书有

        X509v3 Basic Constraints:
            CA:TRUE

我认为 OpenSSL 将其视为一般的“可以出于任何目的验证”扩展。新的IntermediateCAWithTrust 没有X509v3 Basic Constraints,而是有

Trusted Uses:
  Any Extended Key Usage
No Rejected Uses.

有关-addtrust 选项的更多信息,以及可以添加的信任属性类型,请参阅https://www.openssl.org/docs/manmaster/man1/x509.html#TRUST_SETTINGS

该页面底部附近是前面讨论的简明摘要:

basicConstraints 扩展 CA 标志用于确定是否 证书可以用作 CA。如果 CA 标志为真,那么它是 CA,如果 CA 标志为假,则它不是 CA。所有的 CA 都应该有 CA 标志设置为 true。

如果 basicConstraints 扩展不存在,则证书是 被认为是“可能的 CA” 检查其他扩展 根据证书的预期用途。发出警告 在这种情况下,因为证书真的不应该被视为 一个 CA:但是它可以是一个 CA 来解决一些损坏的问题 软件。

因此,简而言之,请确保您的中间 CA 是正确的 CA(在其X509v3 Basic Constraints 中)。这似乎是一个很好的教程(它显式地将中间 CA 生成为 CA):https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html

作为备用计划,您可以始终提供整个链,或者您可以使用-addtrust hack 制作您的中间 CA。

【讨论】:

  • 在您的示例中,似乎有几件事:我认为您不需要 -name ca_y-extensions ext_y 作为您的机器证书,以免它成为 CA。其次,您的服务器加载中间 CA 及其证书。但服务器的证书将根据 客户端的 受信任的 CA 证书进行检查。 TLS 不会自动传输整个链。客户端的受信任 CA 应包括 xx and y。如果客户端只有 x 作为其受信任的 CA,那么服务器必须同时发送“y and machine”(这通常通过将 PEM 连接到一个文件中来完成,作为服务器的“证书”)。
  • 如果您不控制客户端的受信任 CA 列表,那么您将不得不让知名 CA 生成您的服务器证书(例如 VeriSign 或 Comodo),因为大多数客户端都附带 CA 证书对于已经在“受信任的 CA”列表中的知名 CA。
  • 我尝试设置节点的ca 属性,我认为这意味着我控制了客户端的受信任CA 列表。这就是给我这个错误的原因。 nodejs.org/api/tls.html#tls_tls_createsecurecontext_options
  • @JohannesPille:这不是建议。那就是复制 OP 用来尝试诊断问题的测试设置,以便我们可以显示差异。你看到上面写着“让我们重现 OP 的错误案例”的部分吗?
  • 感谢您的出色回答!但是,当我运行第一个脚本时,奇怪的是通过链进行的验证对我不起作用。我检查了证书,结果发现根证书是正确的 X509 v3 证书,但由于某种原因,中间证书是 X509 v1 非 CA 证书。我想知道为什么这对您有用,我该怎么做才能为中间 CA 获得适当的证书。
【解决方案2】:

https://letsencrypt.org/ 非常易于使用且免费。此外,在本地 HTTP 端口上运行不带 SSL 的节点并使用 NGINX 作为 HTTPS 代理。

sudo apt-get install certbot nginx

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/host.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/host.com/privkey.pem;

    access_log /var/log/nginx/host.access.log;
    error_log  /var/log/nginx/host.error.log;

    server_name _;

    gzip on;
    gzip_proxied any;
    gzip_types text/css text/javascript text/xml text/plain application/javascript application/x-javascript application/json;

    location / {
        include             /etc/nginx/proxy_params;
        proxy_pass          http://localhost:8080;
        proxy_read_timeout  90s;
        proxy_redirect      http://localhost:8080 https://www.host.com;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-03
    • 2016-04-20
    • 1970-01-01
    • 2015-03-18
    • 2011-12-26
    • 1970-01-01
    • 2014-08-13
    • 2020-01-20
    相关资源
    最近更新 更多