跨 CA 签发多个证书的 Nginx mTLS 配置

研究过用同一个 CA 签发的服务端和客户端证书的 Nginx mTLS 配置,本文要试验一番服务端和客户端证书由不同 CA 机构签发的情形。这是常有事,比如与客户间采用 mTLS 加密方式,需要文件交付可能是

  1. 客户端证书由甲方生成,发送客户端私钥和证书(或放在一起的 PKCS#12 格式证书)给乙方
  2. 或由乙方生成客户端私钥或证书,乙方把签发用的 CA 证书发给甲方已配置信任链
  3. 甚至服务端,客户端的证书都由甲方生成的情况下也可能使用不同的 CA 签发

下面来测试不同 CA 签发证书的 Nginx mTLS 配置。

今天升级了 ChatGPT 为 Plus 版本,可以用 ChatGPT 4o, 确实是比较强,输入 "mtls 不同 ca 签发的服务端客户端证书在 nginx 中的配置" 提示符产生的内容几乎可以直接作为博文。但本人必须遵循本博客非 AI 产生的原则,只参考 ChatGTP 的答案,关键是一个要自己亲自动手验证并理解每一项配置的功用。

本文需要用到的所有 key 和证书按如下步骤操作

生成 server 和 client 的 CA 证书

分别生成 server-ca.key, server-ca.crt, client-ca.key, client-ca.crt

然后分别用它们签发服务端与客户端证书

用 server ca 签发服务端证书

生成了 server.key, server.crt

注:CN=server.local 是指定的域名,本机或其他机器要访问该 Nginx, 应在 /etc/hosts 中配置 <nginx ip> server.local, 然后就可以解析 server.local 了

用  client ca 签发客户端证书

生成了 client1.key, client1.crt

Nginx 中的 mTLS 配置

先尝试一下错误的配置,在 /etc/nginx/nginx.conf 中如果 server {} 配置是

也就是 ssl_client_certificate 指定为 /certs/server-ca.crt

curl 带上 --cert client1.crt --key client1.key  也访问不了

因为用于签发客户端 client1.key, client1.crt 的 CA 证书未配置在 nginx.conf 中,也就是说它们的证书不被 Nginx 信任

如果把 ssl_client_certificate 换成 /certs/client-ca.crt

就能用 curl 正常访问了

因为 client1.crt 是用 /certs/client-ca.crt 签发的,所以 client1.crt 是被信任的。

配置项 ssl_client_certificate 看上去就是要配置一个客户端证书,其实不对,而是要一个用于签发客户端证书的 CA 的证书。

假如生成更多的 client key  和证书,如  client2.key 和  client2.crt

由于 client2.crt 也是由 client-ca 签发的,所以不用改 nginx.conf 配置,下面 curl 换成了 client2.crt, client2.key 后也没问题

如果再有新 CA 签发的客户端证书,如

生成的 client-x-ca.key, client-x-ca.crt, client3.key, client3.crt

若要让 curl --cert client3.crt --key client3.key https://server.local -k 可工作的话,则可以用 ssl_trusted_certificate 补上用于签下 client3 证书的 CA 证书 client-x-ca.crt

再有更多 CA  签发的客户端证书,字面上的光靠 ssl_client_certificate 和  ssl_trusted_certificate 似乎无能为力了,因为它们都是不可重复的属性。

但是一个 *.crt 文件中可以包含多个证书的内容啊,因此我们需要把多个 CA 的证书合并成一个证书文件,即要让 ssl_trusted_certificate 指定的文件中含多个 CA 证书的内容。下面是简单的合并证书内容的命令

cat client_a-ca.crt client_b-ca.crt client_c-ca.crt > other-clients-ca.crt

合并后,other-clients-ca.crt 中的内容就是下面的格式

再用 ssl_trusted_certificate 指定为该含有多个 CA 证书的文件

该做法实质用 ssl_trusted_certificate 配置的是一个客户端证书信任链,服务端就能会信任 ssl_client_certificate 和 ssl_trusted_certificate 包含的所有 CA 签发的客户端证书。

附:如何让 curl 信任证书

默认是 curl 也是只信任权威的证书,所以对于自签发证书配置的配置端必须用 -k 来强自任凭,没有 -k 就是

那么如何不用 -k  参数还能获取正常响应结果呢?有以下几种办法

--cacert 参数,相当于浏览器导入了第三方可信任证书

即信任服务端 server.crt 证书或用于签发 server.crt 的 CA 证书。信任某个证书的话也必须信任由此证书签发的其他证书,即信任老子就要无条件信任小子。

通过环境变量替换 --cacert 参数

与前面等效。还有一个  --capath  指定目录,用起来稍显复杂,不作深入了。

还可以用 Trust Store, 如 macOS 的 Keychain Access, Windows 的 Certificates, Debian/Ubuntu 的 /etc/ssl/certs/ca-certificates.crt, 和 RHEL/CentOS  的文件 /etc/pki/tls/certs/ca-bundle.crt。

以 Ubuntu 为例,把欲信任的证书内容附加到 /etc/ssl/certs/ca-certificates.crt 后就可以免去 -k, 操作

假设 server-ca.crt 在当前目录中。

对于 Java 程序或很多地方也都有 Trust Store 这个概念,可以顺着这个思路,进行信任某个 CA 所签发的证书。 

本文链接 https://yanbin.blog/client-certs-with-different-ca-nginx-mtls-config/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments