OpenSSL 验证:不同 OpenSSL 版本之间的“0 深度查找时的错误 20:无法获取本地颁发者证书”

Posted

技术标签:

【中文标题】OpenSSL 验证:不同 OpenSSL 版本之间的“0 深度查找时的错误 20:无法获取本地颁发者证书”【英文标题】:OpenSSL verify: "error 20 at 0 depth lookup: unable to get local issuer certificate" between different OpenSSL versions 【发布时间】:2021-06-03 15:58:34 【问题描述】:

对于在 OpenSSL 1.1.1(在 Ubuntu 18.04 上)与 OpenSSL 1.1.1f(在 Ubuntu 20.04 上)之间生成的证书链,我遇到了一个奇怪的验证错误。

这是我的测试环境(两个 Docker 镜像):

docker run -it ubuntu:18.04 /bin/bash docker run -it ubuntu:20.04 /bin/bash

该方案涉及生成一个自签名根 CA,然后是一个或多个颁发的证书。在 Ubuntu 18.04 实例上,结果看起来不错:

root@temp-ubuntu-0:/tmp/cert# openssl version
OpenSSL 1.1.1  11 Sep 2018
root@temp-ubuntu-0:/tmp/cert# openssl verify -CAfile root.cer client.cer
client.cer: OK

在 Ubuntu 20.04 上,出现“在 0 深度查找时出现错误 20:无法获取本地颁发者证书”错误:

root@temp-ubuntu-20-0:/tmp/cert# openssl version
OpenSSL 1.1.1f  31 Mar 2020
root@temp-ubuntu-20-0:/tmp/cert# openssl verify -CAfile root-ca.cer client.cer
C = CA, ST = State, L = City, OU = POC, CN = client
error 20 at 0 depth lookup: unable to get local issuer certificate
error client.cer: verification failed

# Observed the same behaviour with OpenSSL 1.1.1g and 1.1.1i (from nginx Docker images)

以下是采取的步骤:

mkdir -p /tmp/cert
cd /tmp/cert

# Create a ".rnd" file to avoid warnings
openssl rand -writerand ~/.rnd

# Create the root CA private key and certificate
openssl req \
    -new \
    -x509 \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout root-ca.key \
    -out root-ca.cer \
    -days 3650 \
    -subj '/C=CA/ST=State/L=City/OU=POC/OU=Certificate Authorities/CN=POC Root CA' \
    -addext "basicConstraints = CA:TRUE" \
    -addext "subjectKeyIdentifier = hash" \
    -addext "authorityKeyIdentifier = keyid:always, issuer:always" \
    -addext "subjectAltName = DNS:POC Root CA" \
    -addext "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly"

# Create the CSR and private key
openssl req \
    -new \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout server.key \
    -out server.csr \
    -subj "/C=CA/ST=State/L=City/OU=POC/CN=server"

# Confirm the contents of the CSR
openssl req -in server.csr -text -noout

# Create the .conf file
cat > /tmp/cert/server_openssl.conf << EOF
[ v3_attributes ]
basicConstraints = CA:FALSE
subjectAltName   = DNS:server
keyUsage         = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly
extendedKeyUsage = serverAuth
EOF

# Create the certificate
openssl x509 \
    -req \
    -sha256 \
    -CA root-ca.cer \
    -CAkey root-ca.key \
    -in server.csr \
    -out server.cer \
    -days 3650 \
    -set_serial `date +%Y%m%d%H%M%S%N` \
    -extfile /tmp/cert/server_openssl.conf \
    -extensions v3_attributes

# Confirm the contents of the new certificate
openssl x509 -in server.cer -text -noout

# Create the CSR and private key
openssl req \
    -new \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout client.key \
    -out client.csr \
    -subj "/C=CA/ST=State/L=City/OU=POC/CN=client"

# Confirm the contents of the CSR
openssl req -in client.csr -text -noout

# Create the .conf file
cat > /tmp/cert/client_openssl.conf << EOF
[ v3_attributes ]
basicConstraints = CA:FALSE
subjectAltName   = DNS:client
keyUsage         = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly
extendedKeyUsage = clientAuth
EOF

# Create the certificate
openssl x509 \
    -req \
    -sha256 \
    -CA root-ca.cer \
    -CAkey root-ca.key \
    -in client.csr \
    -out client.cer \
    -days 3650 \
    -set_serial `date +%Y%m%d%H%M%S%N` \
    -extfile /tmp/cert/client_openssl.conf \
    -extensions v3_attributes

# Confirm the contents of the new certificate
openssl x509 -in client.cer -text -noout

server.cer 也会出现同样的问题;在一个中起作用,但在另一个中不起作用。

最终目标是在 NGINX 上配置 mTLS。服务器 TLS 部分似乎工作正常,但客户端证书身份验证遇到未解决的问题,导致发现这种情况。希望这不仅仅是一条红鲱鱼。

非常感谢您对此行为的任何见解!

谢谢!

【问题讨论】:

【参考方案1】:

如果将根 CA 拆分为 openssl req/openssl x509 命令而不是根 CA 的单个 openssl req 命令,这似乎可以工作。感觉像一个缺陷,但它的工作原理。使用 OpenSSL 1.1.1f 在 Ubuntu 20.04 上测试。

这是新的命令集:

# Create the root CA CSR and private key
openssl req \
    -new \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout root.key \
    -out root.csr \
    -subj "/C=CA/ST=State/L=City/OU=POC/OU=Certificate Authorities/CN=POC Root CA"

# Create the root CA .conf file
cat > /tmp/cert/root_openssl.conf << EOF
[ v3_attributes ]
basicConstraints     = CA:TRUE
subjectKeyIdentifier = hash
keyUsage             = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly
EOF

# Create the root CA certificate
openssl x509 \
    -req \
    -sha256 \
    -signkey root.key \
    -in root.csr \
    -out root.cer \
    -days 3650 \
    -set_serial `date +%Y%m%d%H%M%S%N` \
    -extfile /tmp/cert/root_openssl.conf \
    -extensions v3_attributes

# Use the AKS namespace name for the server certificate
export SERVER_NAME=echo-namespace-1

# Create the server CSR and private key
openssl req \
    -new \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout server.key \
    -out server.csr \
    -subj "/C=CA/ST=State/L=City/OU=POC/CN=server"

# Confirm the contents of the server CSR
openssl req -in server.csr -text -noout

# Create the server .conf file
cat > /tmp/cert/server_openssl.conf << EOF
[ v3_attributes ]
basicConstraints = CA:FALSE
subjectAltName   = DNS:server
keyUsage         = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
EOF

# Create the server certificate
openssl x509 \
    -req \
    -sha256 \
    -CA root.cer \
    -CAkey root.key \
    -in server.csr \
    -out server.cer \
    -days 3650 \
    -set_serial `date +%Y%m%d%H%M%S%N` \
    -extfile /tmp/cert/server_openssl.conf \
    -extensions v3_attributes

# Confirm the contents of the new server certificate
openssl x509 -in server.cer -text -noout

# Verify the new server certificate against the root CA
openssl verify -CAfile root.cer server.cer

# Create the client CSR and private key
openssl req \
    -new \
    -nodes \
    -sha256 \
    -newkey rsa:4096 \
    -keyout client.key \
    -out client.csr \
    -subj "/C=CA/ST=State/L=City/OU=POC/CN=client"

# Confirm the contents of the client CSR
openssl req -in client.csr -text -noout

# Create the client .conf file
cat > /tmp/cert/client_openssl.conf << EOF
[ v3_attributes ]
basicConstraints = CA:FALSE
subjectAltName   = DNS:client
keyUsage         = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF

# Create the client certificate
openssl x509 \
    -req \
    -sha256 \
    -CA root.cer \
    -CAkey root.key \
    -in client.csr \
    -out client.cer \
    -days 3650 \
    -set_serial `date +%Y%m%d%H%M%S%N` \
    -extfile /tmp/cert/client_openssl.conf \
    -extensions v3_attributes

# Confirm the contents of the new client certificate
openssl x509 -in client.cer -text -noout

# Verify the new client certificate against the root CA
openssl verify -CAfile root.cer client.cer

谢谢大家!

【讨论】:

仔细检查所有证书的有效日期/时间,并检查您正在测试的所有服务器是否通过 NTP 同步到同一源。两步过程生成的有效性日期值可能与一步过程不同,并且服务器上的时间差可能会暴露或隐藏,具体取决于证书生成时间。或者有效期已过,而更高版本的 OpenSSL 捕捉到了它...... 奇怪,这是可能的。我仔细检查过,时间是 NTP 同步的。但是在两个测试的同一系统(Ubuntu 20.04)上,只有第二组命令似乎有效。否则,当在单个命令中生成根 CA 证书时,OpenSSL 仍会导致 error 20 at 0 depth lookup: unable to get local issuer certificate 错误。

以上是关于OpenSSL 验证:不同 OpenSSL 版本之间的“0 深度查找时的错误 20:无法获取本地颁发者证书”的主要内容,如果未能解决你的问题,请参考以下文章

openssl 版本兼容问题 备忘录

openssl 版本兼容问题 备忘录

不同openssl版本的行为差异

nginx升级之openssl 漏洞

OS X 上的 Python 和 OpenSSL 版本参考问题

nginx之升级openssl及自定义nginx版本