技术解析

Nginx 怎么使用不被信任的 CA 验证客户端证书?
0
2021-05-19 19:57:39
idczone

下面的配置里,ca.crt 是一个 untrusted 的 CA,当用 ca.crt 来验证客户端的证书时,Nginx 的 error log 里提示 unable to verify the first certificate,错误码是 21。但其实我用 openssl 命令行用 ca.crt 国外服务器校验客户端证书是 OK 的。哪位高人指点下怎么解决?

server {
    listen 443 ssl;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    server_name www.example.com;
    ssl_certificate server.crt;
    ssl_certificate_key server.key;

    ssl_verify_client on;
    ssl_client_certificate ca.crt;
}

client_certificate 不需要是可信 ca,你的可能是其他错误导致的,比如客户端证书导出的不太对?

Nginx 的官方文档这样说的:"ssl_client_certificate specifies a file with trusted CA certificates in the PEM format used to verify",这个的意思是不是说我的 ca.crt 必须是 trusted ?

我一直是用的自签名的 ca,然后导出的客户端证书啊,正常用,稍等我看下导出证书的脚本。

你用的是什么版本的 nginx 啊,我看新版好像行为不太一样了
ssl_verify_client on | off | optional | optional_no_ca;
The optional_no_ca parameter (1.3.8, 1.2.5) requests the client certificate but does not require it to be signed by a trusted CA certificate. This is intended for the use in cases when a service that is external to nginx performs the actual certificate verification. The contents of the certificate is accessible through the $ssl_client_cert variable.

我以前也自己签,写了个写脚本改 openssl.cnf 文件里的配置就成。
https://github.com/owent-utils/bash-shell/tree/master/Others/x509_ssl_cert
但是现在有 let's encrypt

这个 optional_no_ca 我昨天试了一下,好像不管用,看官方文档的意思,这个选项启用的时候会请求客户端证书但是不要求客户端证书是 trusted 的,但我这个情况其实是我自己的 ca.crt 是 untrusted,现在我就是没找到路子怎么让 Nginx 或者说让我的 Linux server 信任我的这个 ca.crt ,不知道我这么理解对不对?

大佬,我的 openssl 好像是用 yum install 安装的,能不能指教一下你说的这个 cnf 配置在哪里?我找了一下没找到~

http://nginx.org/en/docs/http/ngx_http_ssl_module.html字段。

不过我试了下,我的 nginx version: nginx/1.13.9 还是可以工作的
```
openssl genrsa -out ca.key 4096
openssl req -new -x509 -nodes -sha256 -subj "$subj/CN=Test Root CA" -days 3650 -key ca.key -out ca.crt
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -subj "/CN=Cert Test"
openssl x509 -req -days 3650 -extfile <(printf "keyUsage=digitalSignature") -in client.csr -CA client_ca.crt -CAkey client_ca.key -set_serial 1234 -out client.crt
openssl pkcs12 -export -in client.crt -inkey client.key -name "Admin Cert" -out client.p12 -password pass:123456
```
```
ssl_client_certificate /ssl/client_ca.crt;
ssl_verify_client on;
```

可以用 certbot 自动配置 letsencrypt 证书
全自动的

这个字段之前试过,我加了 ssl_trusted_certificate ca.crt 的配置,并没什么用啊……

理解一下,在你这个例子里,你用 ca 签发了 client_ca,那为什么你的 ssl_client_certificate 里配的是 client_ca.crt 而不是 ca.crt ?

ca -> client_ca -> client
如果用 ca.crt 的话,就是 2 层验证,也就是 ssl_verify_depth 2; 。

用于 SSL 双向认证的 CA 证书不需要也不应该是一个商业 CA。

ssl_client_certificate file;
ssl_verify_client on | off | optional | optional_no_ca;
ssl_verify_depth number;
你只需要关注以上三行
其中第三行的解释
(用于客户端验证的,下同)根 CA ------第 0 层
|
|_____中间 CA(a)-------第 1 层
| |
| |________用户 A 的证书----第 2 层
| |________用户 B 的证书----第 2 层
|_____用户 C 的证书----第 1 层
如果你把 depth 设为 1,只有 C 的证书会被信任;如果设成 2,所有三个用户的证书都被信任。
如果设成 0,则需要将根 CA 换成(唯一的)被信任的用户证书,且只有这个用户被信任。
一般地,如果证书的拓扑结构不是很复杂(压根没有中间 CA),设为 1 即可。

用 OpenSSL 管理 CA,可参考 https://jamielinux.com/docs/openssl-certificate-authority/

感谢!根据你画的图,其实我的情况是这样的,我的 ca.crt 是第 0 层,然后 client 那边的证书都是 ca.crt 签发的,那其实理论上我只要把 ssl_verify_depth 设置成 1 应该就能 work。但实际上,Nginx 的 error log 里提示 client SSL certificate verify error: (7:certificate signature failure),相关的 verify 的 log 如下,大佬能不能帮忙看看什么情况?
2018/08/01 02:52:56 [debug] 6024*9450 verify:1, error:0, depth:1, subject:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email&issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email&02:52:56 [debug] 6024*9450 verify:0, error:7, depth:0, subject:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=Yealink Equipment/CN=$mac/[email&issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email&02:52:56 [debug] 6024*9450 verify:1, error:7, depth:0, subject:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=Yealink Equipment/CN=$mac/[email&issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email protected]"

我开始文件有手动重命名的,重发一下
openssl genrsa -out ca.key 4096
openssl req -new -x509 -nodes -sha256 -subj "$subj/CN=Test Root CA" -days 3650 -key ca.key -out ca.crt
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -subj "/CN=Cert Test"
openssl x509 -req -days 3650 -extfile <(printf "keyUsage=digitalSignature") -in client.csr -CA ca.crt -CAkey ca.key -set_serial 1234 -out client.crt
openssl pkcs12 -export -in client.crt -inkey client.key -name "Admin Cert" -out client.p12 -password pass:123456

ssl_client_certificate /ssl/ca.crt;
ssl_verify_client on;
其实不存在 client_ca.crt 的

yum 安装的 openssl ?
certificate signature failure ?
我先猜个:sha256 你的 openssl 太老了不支持。
你是啥版本的 openssl ?啥版本的发行版?啥版本的 nginx ?

Openssl 版本是 1.0.2k-fips,Nginx 版本是 1.13.8 ……

你看看 Nginx 编译用的是哪个 OpenSSL,是系统自带的还是静态链接了别的版本?

很抱歉,我恐怕很难透过这些信息提供帮助
我检索了 nginx+{错误信息中的数字*}(其余的信息似乎都是 x.509 证书的一些字段),但无法找到任何有帮助的结果。
客户端有什么错误出现?
你可以尝试从客户端 /服务器抓一下包吗?
*:debug 6024|9450|error:7 等


检索 openssl 和 error 7 得到一结果,或有帮助:
https://serverfault.com/questions/653144/openssl-verify-error-7-at-1-depth-lookupcertificate-signature-failure
从这篇问答来看似乎是用户证书签名问题。
使用`openssl x509 -in {path_to_client_cert} -noout -text`看一下客户端证书所用签名算法。
另外,用`openssl verify -CAfile {path_to_client_verify_ca} {path_to_client_cert}`测试下签发的证书有无问题。

找到 root cause 了,是 openssl 版本的问题,client 使用的签名算法在高版本中的 openssl 已经废弃……

> ssl_client_certificate specifies a file with trusted CA certificates in the PEM format used to verify.
他其实是说,你需要指定一个证书作为 trusted CA …
然后这个 trusted CA 用 ssl_client_certificate 来指定…

我贴的地址里有两个文件。一个是执行脚本的.sh 另一个就是 openssl.cnf

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服