无法“openssl 验证”letsencrypt 证书

Unable to `openssl verify' letsencrypt certificate

我使用 Certbot 容器通过 Letsencrypt 生成证书:

$ mkdir /home/$USER/letsencrypt
$ docker run -it --rm -p 80:80 -p 443:443 -v /home/$USER/letsencrypt:/etc/letsencrypt certbot/certbot certonly --standalone --email user@example.com --agree-tos -d example.com

我导航到生成的证书:

$ cd /home/$USER/letsencrypt/live/example.com

我可以验证 chain.pem:

$ openssl verify chain.pem 
chain.pem: OK

而且我可以看到 chain.pem 中的内容:

$ openssl x509 -noout -in chain.pem -subject -issuer
subject=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
issuer=O = Digital Signature Trust Co., CN = DST Root CA X3

我无法验证cert.pem(大概是因为它需要链):

$ openssl verify cert.pem
CN = example.com
error 20 at 0 depth lookup: unable to get local issuer certificate
error cert.pem: verification failed

但我也无法验证 fullchain.pem:

$ openssl verify fullchain.pem
CN = example.com
error 20 at 0 depth lookup: unable to get local issuer certificate
error fullchain.pem: verification failed

证书似乎在浏览器中有效,但在 curl(和 Android http 客户端,这是真正的问题)中失败:

$ curl https://example.com
curl: (60) SSL certificate problem: unable to get local issuer certificate

我仔细检查过 fullchain.pemcert.pemchain.pem 的串联。

所以:我不明白为什么fullchain.pem不验证?

与直觉相反,我终于 openssl verify 通过将根证书添加到链中来工作。感觉 Letsencrypt CA 应该已经可用了,所以我不认为这是正确的做法(欢迎发表评论)。

步骤是:

  • Chrome 开发人员工具 > 安全选项卡 > 查看证书 > 详细信息选项卡 > Select 根证书 ("Builtin Object Token:DST Root CA X3")
  • 点击导出,导出为 Base64-Encoded ASCII,单一证书(我将其命名为 ca.pem

将根连接到链:

$ ca.pem fullchain.pem > cachain.pem

然后验证:

$ openssl verify cachain.pem
cachain.pem: OK

这感觉"wrong"所以我想了解一下这是不是误报。

我从 man verify 中了解到这一点,阅读了 untrusted 的描述。事实证明 untrusted 实际上是您指定证书信任链的方式(当您这样说时似乎违反直觉)。

因此,您需要验证 Letsencrypt 证书的命令是:

openssl verify -untrusted chain.pem cert.pem

其中 cert.pem 是您的证书,chain.pem 是 LE 中间证书。没有必要为此使用 fullchain.pem

我为同样的问题苦苦挣扎了 3 天。但该错误是由于我的 Apache 配置中的配置错误造成的。

我通过 openssl 命令发现 s_client -connect advertentiekracht.nl:443 返回:

Certificate chain
 0 s:/CN=advertentiekracht.nl
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3

包括"Unable to get local issuer certificate"

命令:[root@srv ssl]# openssl x509 -noout -in /etc/letsencrypt/live/advertentiekracht.nl/chain.pem -subject -issuer 显示丢失的链:

subject= /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
issuer= /O=Digital Signature Trust Co./CN=DST Root CA X3

我当然不熟悉openssl和证书。肯定有很多原因导致“无法获得本地颁发者证书。但在你像我一样开始挖掘之前,请检查你的 http 服务器配置。对我来说就是 Apache。我在定义 SSL 证书 hocus pocus 的地方有拼写错误。httpd 标记下面的错误行

        SSLCertificateFile /etc/letsencrypt/live/advertentiekracht.nl/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/advertentiekracht.nl/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

注意第一行 SSLCertificateFile 应该是 SSLCertificateChainFile,我错过了对 cert.pem 和 chain.pem 的引用。下面几行解决了我的问题:

        SSLCertificateChainFile /etc/letsencrypt/live/advertentiekracht.nl/fullchain.pem
    SSLCertificateFile /etc/letsencrypt/live/advertentiekracht.nl/cert.pem
    SSLCertificateChainFile /etc/letsencrypt/live/advertentiekracht.nl/chain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/advertentiekracht.nl/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

结果,一个完整的链:

    Certificate chain
 0 s:/CN=advertentiekracht.nl
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3

问题是 certbot 颁发的全链中的最后一个证书是由过期的根证书颁发的证书(json 中的输出以说明链中的每个证书)截至 2021 年 9 月 30 日( https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/)

[
  {
    "subject": {
      "commonName": "..."
    },
    "issuer": {
      "countryName": "US",
      "organizationName": "Let's Encrypt",
      "commonName": "R3"
    },
    "version": 3,
    ...
    },
    "OCSP": [
      "http://r3.o.lencr.org"
    ],
    "caIssuers": [
      "http://r3.i.lencr.org/"
    ]
  },
  {
    "subject": {
      "countryName": "US",
      "organizationName": "Let's Encrypt",
      "commonName": "R3"
    },
    "issuer": {
      "countryName": "US",
      "organizationName": "Internet Security Research Group",
      "commonName": "ISRG Root X1"
    },
    "version": 3,
    "serialNumber": "912B084ACF0C18A753F6D62E25A75F5A",
    "notBefore": "Sep  4 00:00:00 2020 GMT",
    "notAfter": "Sep 15 16:00:00 2025 GMT",
    "caIssuers": [
      "http://x1.i.lencr.org/"
    ],
    "crlDistributionPoints": [
      "http://x1.c.lencr.org/"
    ]
  },
  {
    "subject": {
      "countryName": "US",
      "organizationName": "Internet Security Research Group",
      "commonName": "ISRG Root X1"
    },
    "issuer": {
      "organizationName": "Digital Signature Trust Co.",
      "commonName": "DST Root CA X3"
    },
    "version": 3,
    "serialNumber": "4001772137D4E942B8EE76AA3C640AB7",
    "notBefore": "Jan 20 19:14:03 2021 GMT",
    "notAfter": "Sep 30 18:14:03 2024 GMT",
    "caIssuers": [
      "http://apps.identrust.com/roots/dstrootcax3.p7c"
    ],
    "crlDistributionPoints": [
      "http://crl.identrust.com/DSTROOTCAX3CRL.crl"
    ]
  }
]

请注意,链中的最后一项是由 DST Root CA X3 颁发的,如果您获取 http://apps.identrust.com/roots/dstrootcax3.p7c,您会看到它是新近过期的 DST Root CA X3 证书。

所以你得到

$ openssl verify -CAfile fullchain.pem fullchain.pem
server.pem: O = Digital Signature Trust Co., CN = DST Root CA X3
error 10 at 1 depth lookup:certificate has expired
O = Digital Signature Trust Co., CN = DST Root CA X3
error 10 at 3 depth lookup:certificate has expired
OK

但是如果您从 fullchain.pem 中删除最后一个证书并将输出放入 chain.pem

$ openssl verify -CAfile chain.pem fullchain.pem
server.pem: OK

(注意验证只检查 fullchain.pem 中的第一个证书)

对于最新版本的 openssl,您可以使用 -partial_chain-trusted_first,但这些在 MacOS 上安装的 openssl 上不可用。

我这里有几个pem处理工具:https://gitlab.com/Blockdaemon/pem2json