V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
xudzhang
V2EX  ›  NGINX

Nginx 怎么使用不被信任的 CA 验证客户端证书?

  •  
  •   xudzhang · 2018-08-01 09:59:01 +08:00 · 6388 次点击
    这是一个创建于 2305 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下面的配置里,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;
    }
    
    26 条回复    2018-08-02 13:39:01 +08:00
    virusdefender
        1
    virusdefender  
       2018-08-01 10:05:55 +08:00
    client_certificate 不需要是可信 ca,你的可能是其他错误导致的,比如客户端证书导出的不太对?
    xudzhang
        2
    xudzhang  
    OP
       2018-08-01 10:08:44 +08:00
    @virusdefender Nginx 的官方文档这样说的:"ssl_client_certificate specifies a file with trusted CA certificates in the PEM format used to verify",这个的意思是不是说我的 ca.crt 必须是 trusted ?
    virusdefender
        3
    virusdefender  
       2018-08-01 10:15:06 +08:00   ❤️ 1
    @xudzhang #2 我一直是用的自签名的 ca,然后导出的客户端证书啊,正常用,稍等我看下导出证书的脚本。
    virusdefender
        4
    virusdefender  
       2018-08-01 10:27:25 +08:00
    你用的是什么版本的 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.
    owt5008137
        5
    owt5008137  
       2018-08-01 10:29:34 +08:00 via Android   ❤️ 1
    @virusdefender 我以前也自己签,写了个写脚本改 openssl.cnf 文件里的配置就成。
    https://github.com/owent-utils/bash-shell/tree/master/Others/x509_ssl_cert

    但是现在有 let's encrypt
    xudzhang
        6
    xudzhang  
    OP
       2018-08-01 10:32:01 +08:00
    @virusdefender 这个 optional_no_ca 我昨天试了一下,好像不管用,看官方文档的意思,这个选项启用的时候会请求客户端证书但是不要求客户端证书是 trusted 的,但我这个情况其实是我自己的 ca.crt 是 untrusted,现在我就是没找到路子怎么让 Nginx 或者说让我的 Linux server 信任我的这个 ca.crt ,不知道我这么理解对不对?
    xudzhang
        7
    xudzhang  
    OP
       2018-08-01 10:32:45 +08:00
    @owt5008137 大佬,我的 openssl 好像是用 yum install 安装的,能不能指教一下你说的这个 cnf 配置在哪里?我找了一下没找到~
    Zakun
        8
    Zakun  
       2018-08-01 10:35:39 +08:00
    virusdefender
        9
    virusdefender  
       2018-08-01 10:35:40 +08:00
    不过我试了下,我的 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;
    ```
    SirLostWhite
        10
    SirLostWhite  
       2018-08-01 10:35:50 +08:00
    可以用 certbot 自动配置 letsencrypt 证书
    全自动的
    xudzhang
        11
    xudzhang  
    OP
       2018-08-01 10:39:07 +08:00
    @Zakun 这个字段之前试过,我加了 ssl_trusted_certificate ca.crt 的配置,并没什么用啊……
    xudzhang
        12
    xudzhang  
    OP
       2018-08-01 10:41:39 +08:00
    @virusdefender 理解一下,在你这个例子里,你用 ca 签发了 client_ca,那为什么你的 ssl_client_certificate 里配的是 client_ca.crt 而不是 ca.crt ?
    msg7086
        13
    msg7086  
       2018-08-01 10:50:06 +08:00
    ca -> client_ca -> client
    如果用 ca.crt 的话,就是 2 层验证,也就是 ssl_verify_depth 2; 。
    Hardrain
        14
    Hardrain  
       2018-08-01 10:53:10 +08:00   ❤️ 1
    用于 SSL 双向认证的 CA 证书不需要也不应该是一个商业 CA。
    Hardrain
        15
    Hardrain  
       2018-08-01 11:09:26 +08:00   ❤️ 1
    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 即可。
    Hardrain
        16
    Hardrain  
       2018-08-01 11:10:23 +08:00
    xudzhang
        17
    xudzhang  
    OP
       2018-08-01 11:17:52 +08:00
    @Hardrain 感谢!根据你画的图,其实我的情况是这样的,我的 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#0: *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 protected]", issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email protected]"

    2018/08/01 02:52:56 [debug] 6024#0: *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 protected]", issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email protected]"

    2018/08/01 02:52:56 [debug] 6024#0: *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 protected]", issuer:"/C=CN/ST=Fujian/L=Xiamen/O=Yealink Network Technology Co.,Ltd./OU=yealink.com/CN=Yealink Equipment Issuing CA/[email protected]"
    virusdefender
        18
    virusdefender  
       2018-08-01 11:53:19 +08:00
    我开始文件有手动重命名的,重发一下

    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 的
    msg7086
        19
    msg7086  
       2018-08-01 13:24:50 +08:00
    yum 安装的 openssl ?
    certificate signature failure ?

    我先猜个:sha256 你的 openssl 太老了不支持。
    你是啥版本的 openssl ?啥版本的发行版?啥版本的 nginx ?
    xudzhang
        20
    xudzhang  
    OP
       2018-08-01 13:58:31 +08:00
    @msg7086 Openssl 版本是 1.0.2k-fips,Nginx 版本是 1.13.8 ……
    msg7086
        21
    msg7086  
       2018-08-01 16:38:34 +08:00
    @xudzhang 你看看 Nginx 编译用的是哪个 OpenSSL,是系统自带的还是静态链接了别的版本?
    Hardrain
        22
    Hardrain  
       2018-08-01 21:46:36 +08:00
    @xudzhang 很抱歉,我恐怕很难透过这些信息提供帮助

    我检索了 nginx+{错误信息中的数字*}(其余的信息似乎都是 x.509 证书的一些字段),但无法找到任何有帮助的结果。
    客户端有什么错误出现?
    你可以尝试从客户端 /服务器抓一下包吗?

    *:debug 6024|9450|error:7 等
    Hardrain
        23
    Hardrain  
       2018-08-01 21:53:16 +08:00   ❤️ 1
    @xudzhang
    检索 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}`测试下签发的证书有无问题。
    xudzhang
        24
    xudzhang  
    OP
       2018-08-02 10:26:18 +08:00
    @Hardrain 找到 root cause 了,是 openssl 版本的问题,client 使用的签名算法在高版本中的 openssl 已经废弃……
    xierch
        25
    xierch  
       2018-08-02 12:09:03 +08:00
    > ssl_client_certificate specifies a file with trusted CA certificates in the PEM format used to verify.

    他其实是说,你需要指定一个证书作为 trusted CA …
    然后这个 trusted CA 用 ssl_client_certificate 来指定…
    owt5008137
        26
    owt5008137  
       2018-08-02 13:39:01 +08:00 via Android
    @xudzhang 我贴的地址里有两个文件。一个是执行脚本的.sh 另一个就是 openssl.cnf
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4613 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 75ms · UTC 04:01 · PVG 12:01 · LAX 20:01 · JFK 23:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.