使用 openssl 验证证书链

我正在构建一个包含以下组件的证书链:

Root Certificate - Intermediate Certificate - User Certificate

根证书是一个自签署的证书,中级证书由根签署,用户由中级签署。

现在我想通过根证书验证用户证书是否有锚。

openssl verify -verbose -CAfile RootCert.pem Intermediate.pem

验证是确定的。在下一步中,我验证用户证书与

openssl verify -verbose -CAfile Intermediate.pem UserCert.pem

验证表明

error 20 at 0 depth lookup:unable to get local issuer certificate

怎么了?

639293 次浏览

来自 verify文档:

如果找到的证书是其自己的发行者,则假定它是根 CA。

换句话说,根 CA 需要进行自签名以验证才能正常工作。这就是为什么你的第二个命令失败了。试试这个:

openssl verify -CAfile RootCert.pem -untrusted Intermediate.pem UserCert.pem

它将在一个命令中验证您的整个链。

这是 cat为数不多的合法工作之一:

openssl verify -verbose -CAfile <(cat Intermediate.pem RootCert.pem) UserCert.pem

更新:

正如 Greg Smethells 在评论中指出的,该命令隐式地信任 Intermediate.pem。我建议阅读 格雷格的推荐信的第一部分(第二部分专门介绍 pyOpenSSL,与本问题无关)。

如果这篇文章不见了,我会引用其中的重要段落:

不幸的是,实际上是根/自签名的“中间”证书 当使用所给出的推荐命令时,将被视为可信 CA 以上:

$openssl 確認-CAFile < (cat geotrust _ global _ ca. pem 舞弊者 _ ca. pem) 假冒某科技公司 假的 _ some techcompany _ from _ rogue _ ca.com. pem: 好的

一旦遇到根证书,openssl 似乎将停止验证链,如果它是自签名的,也可能是 Intermediate.pem。在这种情况下,不考虑 RootCert.pem。因此,在依赖上面的命令之前,请确保 Intermediate.pem 来自受信任的源。

问题是,openssl -verify没有做这项工作。

正如 Priyadi 提到的 openssl -verify在第一个自签名证书处停止,因此您并不真正验证链,因为中间证书通常是自签名的。

我假设您希望在尝试将证书文件安装到高效的 Web 服务之前,能够101% 地确认它们是正确的。这个食谱就是用来做飞行前检查的。

请注意,彼得的回答是正确的,但是 openssl -verify的输出是没有线索的,一切 真的工作后。是的,它可能会发现一些问题,但不是全部。

下面是一个脚本,它执行在将证书链安装到 Apache 之前验证证书链的工作。也许这可以通过一些更神秘的 OpenSSL 魔术得到增强,但我不是 OpenSSL 大师,我有以下作品:

#!/bin/bash
# This Works is placed under the terms of the Copyright Less License,
# see file COPYRIGHT.CLL.  USE AT OWN RISK, ABSOLUTELY NO WARRANTY.
#
# COPYRIGHT.CLL can be found at http://permalink.de/tino/cll
# (CLL is CC0 as long as not covered by any Copyright)


OOPS() { echo "OOPS: $*" >&2; exit 23; }


PID=
kick() { [ -n "$PID" ] && kill "$PID" && sleep .2; PID=; }
trap 'kick' 0


serve()
{
kick
PID=
openssl s_server -key "$KEY" -cert "$CRT" "$@" -www &
PID=$!
sleep .5    # give it time to startup
}


check()
{
while read -r line
do
case "$line" in
'Verify return code: 0 (ok)')   return 0;;
'Verify return code: '*)    return 1;;
#   *)  echo "::: $line :::";;
esac
done < <(echo | openssl s_client -verify 8 -CApath /etc/ssl/certs/)
OOPS "Something failed, verification output not found!"
return 2
}


ARG="${1%.}"
KEY="$ARG.key"
CRT="$ARG.crt"
BND="$ARG.bundle"


for a in "$KEY" "$CRT" "$BND"
do
[ -s "$a" ] || OOPS "missing $a"
done


serve
check && echo "!!! =========> CA-Bundle is not needed! <========"
echo
serve -CAfile "$BND"
check
ret=$?
kick


echo
case $ret in
0)  echo "EVERYTHING OK"
echo "SSLCertificateKeyFile $KEY"
echo "SSLCertificateFile    $CRT"
echo "SSLCACertificateFile  $BND"
;;
*)  echo "!!! =========> something is wrong, verification failed! <======== ($ret)";;
esac


exit $ret

请注意,EVERYTHING OK之后的输出是 Apache 设置,因为使用 NginXhaproxy的人通常也可以完全读取和理解这个设置;)

这里有一个 GitHub Gist,可能有一些更新

这个脚本的先决条件:

  • 像往常一样,在 /etc/ssl/certs中有可信的 CA 根数据,例如在 Ubuntu 上
  • 创建一个存储3个文件的目录 DIR:
    • 包含证书的 DIR/certificate.crt
    • DIR/certificate.key,它包含您的网络服务的密钥(没有密码)
    • 包含 CA-Bundle 的 DIR/certificate.bundle
  • 现在运行这个脚本: ./check DIR/certificate(假设这个脚本在工作目录中被命名为 check)
  • 有一个非常不可能的情况,脚本输出 CA-Bundle is not needed。这意味着,您(读取: /etc/ssl/certs/)已经信任签名证书。但这在万维网上是极不可能的。
  • 对于此测试端口4433,必须在您的工作站上未使用。最好只在安全环境下运行,因为4433端口很快就会对公众开放,公众可能会在敌对环境中看到外部连接。

如何创建 certificate.bundle文件?

在万维网上,信任链通常是这样的:

  • 来自 /etc/ssl/certs的可信证书
  • 未知的中间证书,可能由另一个 CA 交叉签名
  • 你的证书(certificate.crt)

现在,评估从下到上进行,这意味着,首先读取您的证书,然后需要未知的中间证书,然后可能需要交叉签名证书,然后查询 /etc/ssl/certs以找到合适的可信证书。

Ca-bundle 必须按照正确的处理顺序组成,这意味着第一个需要的证书(签署证书的中间证书)首先出现在 bundle 中。然后需要交叉签名证书。

通常您的 CA (签署证书的权威机构)已经提供了这样一个合适的 CA-bundle 文件。如果没有,则需要挑选所有需要的中间证书,并将它们 cat放在一个文件中(在 Unix 上)。在 Windows 上,您只需打开一个文本编辑器(如 notepad.exe) ,并将证书粘贴到文件中,首先需要在顶部,然后其他。

还有一件事。文件需要采用 PEM 格式。一些 CA 发出 DER (二进制)格式。PEM 很容易识别: 它是 ASCII 可读的。有关如何将某些内容转换为 PEM 的更多信息,请参见 如何将.crt 转换为.pem并沿着黄砖路走。

例如:

你有:

  • 签署您的 certificate.crt的中间证书
  • 另一个中间证书,烧焦了 intermediate2.crt
  • crossigned.crt是来自另一个 CA 的交叉签名证书,它对 intermediate1.crt进行了签名
  • crossintermediate.crt是另一个签名为 crossigned.crt的 CA 的另一个中间体(你可能永远不会看到这样的东西)

那么正确的 cat应该是这样的:

cat intermediate2.crt intermediate1.crt crossigned.crt crossintermediate.crt > certificate.bundle

您如何找出需要或不需要哪些文件以及按照哪些顺序?

试验,直到 check告诉你一切正常。它就像一个解谜的电脑益智游戏。每一个。单身。时间。即使是专业人士。但是每次你需要这样做的时候,你都会变得更好。所以你绝对不是唯一一个承受这些痛苦的人。这是 SSL,你知道吗?SSL 可能是我在30多年的专业系统管理工作中见过的最糟糕的设计之一。有没有想过为什么加密在过去的30年里没有成为主流?这就是原因。不必多说。

在完全相同的问题上打破了整整一天,没有事先知道 SSL 证书,我下载了 密钥存储管理器和导入我的密钥存储到它,并得到了证书链的清晰可视化。

截图:

enter image description here

您可以使用 openssl 轻松地验证证书链。完整链将包括 CA 证书,因此您应该看到有关 CA 和证书本身的详细信息。

Openssl x509-in fullchain.pem-text-noout

我必须验证一个 let 加密证书,我是这样做的:

  1. 让信任链加密下载 root-cert 和 Intermediate-cert。
  2. 发出以下命令:

    $ openssl verify -CAfile letsencrypt-root-cert/isrgrootx1.pem.txt -untrusted letsencrypt-intermediate-cert/letsencryptauthorityx3.pem.txt /etc/letsencrypt/live/sitename.tld/cert.pem
    /etc/letsencrypt/live/sitename.tld/cert.pem: OK
    

如果只想验证 UserCert.pem实际上Intermediate.pem发行人,请执行以下操作(示例使用: OpenSSL 1.1.1) :

openssl verify -no-CAfile -no-CApath -partial_chain -trusted Intermediate.pem UserCert.pem

你会得到:

UserCert.pem: OK

或者

UserCert.pem: verification failed

openssl verify不像 SSL 客户机那样处理证书链。你可以通过三个步骤来复制他们所做的:

(cat cert.pem chain.pem | diff -q fullchain.pem -) && \
openssl verify chain.pem && \
openssl verify -CAfile chain.pem cert.pem

这将确认 fullchain.pem = = cert.pem + chain.pem,并且根据系统上安装的 CA (通常在 ca-certificates包的 /etc/ssl/certs中)它是合法的。


如果您试图验证 letscrypt/ACME,请注意,letscrypt 为每个域提供了四个文件:

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem

其中,你的证书对是(fullchain.pemprivkey.pem) ,没有(cert.pemprivkey.pem)或(chain.pemprivkey.pem)。

例如,在 nginx.conf 中,您将输入:

ssl_certificate /etc/letsencrypt/domain.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/domain.example.com/privkey.pem;

如果你有以根证书开始的整个 CA 链,例如包含证书的 cachain.pem,则检查

openssl verify -CAfile cachain.pem -untrusted cachain.pem mycert.pem

相当于(因为 openssl 将只读取来自 CAfile的第一个证书)

openssl verify -CAfile root.pem -untrusted cachain.pem mycert.pem

会完成任务的。一些消息来源提到,openssl verify接受几个 -untrusted选项,但这不适合我的一些版本的 openssl