Nginx | HTTPS 加密传输:客户端与Nginx服务端 SSL 双向认证实践

知识是人生的灯塔,只有不断学习,才能照亮前行的道路

📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。

本地客户端 与 Nginx 服务端 SSL 双向认证指令浅析与配置

描述: 上文《Nginx | 核心知识150讲,百万并发下性能优化之SSL证书签发与HTTPS加密传输实践笔记》,介绍了如何在 Nginx 中配置 HTTPS 服务端单向认证,即证书配置在服务端,客户端不需要提供证书,但是这样的配置方式存在一定的安全隐患,因为客户端与服务端之间的通信并没有进行双向身份验证,有可能存在中间人攻击和数据泄露等问题。因此,为了保证数据在传输过程中不被泄露和篡改,且应用间的数据传输敏感的场景之下,通常会采用 SSL 双向认证的方式来进行身份验证,简单来说:服务端需要提供CA证书来对客户端证书进行身份验证的过程。

温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_wwnjk3rmwroa.html

同样的,在 Nginx 中配置客户端与服务端 SSL 双向认证,除开上一小节的指令,也涉及到以下几个指令:

指令参数

  • ssl_client_certificate 指令用于指定具有PEM格式的受信任CA证书签发的客户端证书,若启用了ssl_stapling,则该文件用于验证客户端证书和OCSP响应。
go 复制代码
Syntax: ssl_client_certificate file;
Default: ---
Context: http, server
  • ssl_trusted_certificate 指令用于指定具有PEM格式的受信任CA证书的文件,该文件用于验证客户端证书,与ssl_client_certificate 设置的证书不同,这些证书的列表不会发送给客户端。
go 复制代码
Syntax: ssl_trusted_certificate file;
Default:---
Context: http, server
  • ssl_verify_client 指令用于指定是否启用客户端证书验证,可选值为 off、on 或 optional, 验证结果存储在$ssl_client_verify 变量中。
go 复制代码
Syntax: ssl_verify_client on | off | optional | optional_no_ca;
Default: ssl_verify_client off;
Context: http, server

# 参数说明
  on 启用客户端证书验证
  off 禁用客户端证书验证
  optional 请求客户端证书并验证证书是否存在
  optional_no_ca 请求客户端证书,但不要求它由受信任的CA证书签名
  • ssl_verify_depth 指令用于设置客户端证书链中的验证深度,默认值为1。
go 复制代码
Syntax: ssl_verify_depth number;
Default: ssl_verify_depth 1;
Context: http, server
  • ssl_ocsp 指令用于启用客户端证书链的OCSP验证,该功能允许服务器在握手期间验证客户端证书的有效性,而不是等到建立连接后才进行。
go 复制代码
Syntax: ssl_ocsp on | off | leaf;
Default: ssl_ocsp off;
Context: http, server
# 参数说明
  on 启用客户端证书链的OCSP验证
  off 禁用客户端证书链的OCSP验证
  leaf 只验证客户端证书的签名,不验证整个链

# 示例:
# 若要解析OCSP响应程序主机名,还应指定resolver指令
ssl_verify_client on;
ssl_ocsp          on;
resolver          192.168.;
  • ssl_ocsp_cache 指令设置存储用于OCSP验证的客户端证书状态的该高速缓存的名称和大小。该高速缓存在所有工作进程之间共享。具有相同名称的缓存可以在多个虚拟服务器中使用。
go 复制代码
Syntax: ssl_ocsp_cache off | [shared:name:size];
Default: ssl_ocsp_cache off;
Context: http, server
  • ssl_ocsp_responder 指令用于设置客户端证书链的OCSP响应程序的URL,该选项仅在启用ssl_ocsp on;后才有效。如果设置了此参数,则Nginx将尝试从指定的URL获取OCSP响应程序,并将其用作验证客户端证书的有效性的替代方法。
go 复制代码
Syntax:  ssl_ocsp_responder url;
Default:  ---
Context:  http, server

另外,http_ssl_module 模块还提供了一些内置变量,可用于输出客户端证书中的信息,如下所示:

  • $ssl_server_name: 返回通过SNI(1.7.0)请求的服务器名称;

  • $ssl_alpn_protocol: 返回ALPN在SSL握手期间选择的协议,否则返回空字符串(1.21.4)

  • $ssl_protocol: 返回使用的SSL/TLS协议版本名称(例如,TLSv1.2)

  • $ssl_cipher: 返回使用的SSL/TLS加密套件名称

  • $ssl_ciphers: 返回客户端支持的密码列表(1.11.7)

  • $ssl_client_escaped_cert: 以PEM格式(urlencoded)返回已建立SSL连接的客户端证书(1.13.5);

  • $ssl_client_cert: 以PEM格式返回已建立的SSL连接的客户端证书,不推荐使用,建议使用 ssl_client_escaped_cert 变量。

  • $ssl_client_raw_cert: 以PEM格式返回已建立SSL连接的客户端证书;

  • $ssl_client_fingerprint: 返回已建立SSL连接的客户端证书的SHA1指纹(1.7.1);

  • $ssl_client_i_dn: 根据RFC 2253(1.11.6)返回已建立SSL连接的客户端证书的"issuer DN"字符串;

  • $ssl_client_s_dn: 根据RFC 2253(1.11.6)返回已建立SSL连接的客户端证书的"subject DN"字符串;

  • $ssl_client_serial: 返回已建立SSL连接的客户端证书的序列号;

  • $ssl_client_sigalg: 返回已建立SSL连接的客户端证书的签名算法(1.29.3), 仅当使用OpenSSL 3.5或更高版本时才支持该变量。

  • $ssl_client_v_start$ssl_client_v_end: 返回已建立SSL连接的客户端证书的开始和结束时间;

  • ssl_client_v_remain: 返回已建立SSL连接的客户端证书剩余有效期(1.7.1);

  • $ssl_client_verify: 返回客户端证书验证结果,可能的值有:SUCCESS、FAILED: 证书未发送(1.5.7)、FAILED: 证书被拒绝(1.5.7)、NONE 或 FAILED: 未知原因。

  • $ssl_curve: 返回用于SSL握手密钥交换过程的协商曲线(1.21.5)。

  • $ssl_curves: 返回客户端支持的曲线列表(1.11.7)

  • $ssl_early_data: 如果使用TLS 1.3早期数据且握手未完成,则返回"1",否则返回""(1.15.3).

  • $ssl_ech_outer_server_name: 如果接受TLS 1.3 ECH,则返回通过SNI请求的公共服务器名称,否则返回""(1.29.4);

  • $ssl_ech_status: 返回TLS 1.3 ECH处理的结果:"FAQUE"、"EQUEND"、"GREASE"、"SUCCESS"或"NOT_TRIED"(1.29.4);

  • $ssl_session_id: 返回当前的SSL会话ID(1.13.0),注意其受到SSL会话缓存大小以及时间限制);

  • $ssl_session_reused: 如果SSL会话被重用,则返回"r",或者"."否则(1.5.11)。

  • $ssl_sigalg: 返回已建立SSL连接的服务器证书的签名算法(1.29.3)。

好了,接下来我们接入正题,来看看如何在 Nginx 中配置 客户端 与 Nginx 服务端双向认证。

示例演示

步骤 01.将上一篇《Nginx | 核心知识150讲,百万并发下性能优化之SSL证书签发与HTTPS加密传输实践笔记》文章中生成的 ca、server、client 证书和私钥文件拷贝到 Nginx 的 /usr/local/nginx/certs/ 目录下,如下所示:

go 复制代码
cp /tmp/certs/ca.crt /tmp/certs/ca.key /usr/local/nginx/certs/

cp /tmp/certs/server.crt /tmp/certs/server.key /tmp/certs/server_encrypted.key /tmp/certs/ssl_password.txt /usr/local/nginx/certs/

cp /tmp/certs/client.crt /tmp/certs/client.key /usr/local/nginx/certs/

步骤 02.在 Nginx 配置文件中添加 SSL 相关指令,如下所示:

go 复制代码
tee /usr/local/nginx/conf.d/ssl_client_server.conf <<'EOF'
server {
  listen 80;
# 监听 443 端口,启用 SSL 
  listen 443 ssl;
# 虚拟主机服务器名称
  server_name server.weiyigeek.top;
  charset utf-8;
  default_type text/plain;

# 开起 HTTP/2 支持
  http2 on;

# 日志文件
  access_log /var/log/nginx/server.log main;
  error_log /var/log/nginx/server.err.log debug;

# SSL 证书文件
  ssl_certificate /usr/local/nginx/certs/server.crt;
  ssl_certificate_key /usr/local/nginx/certs/server.key;

# 配置加密的 SSL 证书密钥文件(根据需求选择)
# ssl_certificate_key /usr/local/nginx/certs/server_encrypted.key;
# ssl_password_file /usr/local/nginx/certs/ssl_password.txt;

# 配置可信的 CA 证书文件
# ssl_trusted_certificate /usr/local/nginx/certs/ca.crt;

# 配置客户端证书验证
  ssl_client_certificate /usr/local/nginx/certs/ca.crt;
  ssl_verify_client on;

# 指定客户端证书到根证书的深度 
  ssl_verify_depth 2;

# 支持的 SSL/TLS 协议版本
  ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

# 支持的 SSL/TLS 加密套件
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;

# SSL 会话缓存
  ssl_session_cache shared:SSL:10m;
# SSL 会话超时时间
  ssl_session_timeout 10m;
# 优先使用服务器端支持的加密套件
  ssl_prefer_server_ciphers on;


# 强制使用 HTTPS 访问
  add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload" always;

  location / {
    root /usr/local/nginx/html;
    index index.html;
  }

  location /certificate {
    return 200 '客户端证书验证结果:$ssl_client_verify\nssl_server_name: $ssl_server_name\nssl_protocol: $ssl_protocol\nssl_client_fingerprint: $ssl_client_fingerprint\nssl_cipher: $ssl_cipher\nssl_client_i_dn: $ssl_client_i_dn\nssl_client_s_dn: $ssl_client_s_dn\nssl_client_v_start: $ssl_client_v_start\nssl_client_v_end: $ssl_client_v_end\nssl_client_v_remain: $ssl_client_v_remain\nssl_session_id: $ssl_session_id\nssl_client_cert:\n $ssl_client_cert';
  }
}
EOF

步骤 03.配置完成后,重启 Nginx 服务,并硬解析站点域名指向服务器 IP 地址,使用浏览器访问 https://server.weiyigeek.top/ 测试 https 是否配置成功,以及有何效果?由于客户端未发送证书,所以会提示" 400 Bad Request No required SSL certificate was sent" ,从而无法正常访问到业务系统,如下图所示:

go 复制代码
nginx -s reload

# Linux
echo '10.20.172.214 server.weiyigeek.top' >> /etc/hosts

# Windows
# C:\Windows\System32\drivers\etc\hosts
10.20.172.214 server.weiyigeek.top

weiyigeek.top-No required SSL certificate was sent图

步骤 04.接下来根据 client.keyclient.crt 生成 pkcs12 格式的证书密钥打包文件,下载到机器后再将客户端证书导入到 Windows 系统证书管理器中,在 Windows 系统中,双击 client.pfx 文件导入到当前用户>个人/或者自动选择即可,或者 Ctrl + R 输入 certmgr.msc 打开证书管理器,选择"导入",然后选择 client.pfx 文件即可。

go 复制代码
$ openssl pkcs12 -export -inkey client.key -in client.crt -out client.pfx 
Enter Export Password:
Verifying - Enter Export Password:

weiyigeek.top-导入客户端证书到windows系统图

步骤 05.导入完毕后,在浏览器中访问 https://server.weiyigeek.top/ 如下图所示,选择导入的客户端证书。
weiyigeek.top-浏览器中选择客户端证书图

步骤 06.最后再次刷新访问 https://server.weiyigeek.top/certificate 测试一下客户端证书是否可正常访问站点,如图可以看到客户端证书验证结果为 SUCCESS,以及 SSL 加密套件以及客户端证书信息。
weiyigeek.top-验证客户端双向认证图

步骤 07.若需要使用类似于 Postman 或 Apifox 客户端工具访问,双向认证站点时,需要导入客户端证书密码或者p12格式证书,这里以 Apifox 客户端工具为例,如下所示:
weiyigeek.top-在Apifox 客户端工具中配置认证图

知识扩展:有时候生产环境中想获取到服务端的公钥信息,可以使用 openssl s_client 命令进行获取:

go 复制代码
$ openssl s_client -connect server.weiyigeek.top:443 </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >./server.crt
depth=0 C = CN, ST = Chongqing, L = Chongqing, O = WeiyiGeek, OU = Nginx Server, CN = server.weiyigeek.top
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = CN, ST = Chongqing, L = Chongqing, O = WeiyiGeek, OU = Nginx Server, CN = server.weiyigeek.top
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 C = CN, ST = Chongqing, L = Chongqing, O = WeiyiGeek, OU = Nginx Server, CN = server.weiyigeek.top
verify return:1
DONE

$ ls -alh server.crt
-rw-r--r-- 1 root root 1.5K Dec 16 11:33 server.crt

至此,Nginx 服务端与客户端双向认证配置完成,是不是很简单呀,你也赶快试试吧!

END

加入:作者【全栈工程师修炼指南】知识星球

『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。

Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?

✅ 将获得作者最新工作学习实践文章以及网盘资源。

✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)

✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。

✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记

作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章

若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!

相关推荐
Dream of maid22 分钟前
Linux(下)
linux·运维·服务器
齐鲁大虾25 分钟前
统信系统UOS常用命令集
linux·运维·服务器
Benszen44 分钟前
Docker容器化技术实战指南
运维·docker·容器
ZzzZZzzzZZZzzzz…1 小时前
Nginx 平滑升级:从 1.26.3 到 1.28.0,用户无感知
linux·运维·nginx·平滑升级·nginx1.26.3·nginx1.28.0
fengfuyao9851 小时前
基于STM32的4轴步进电机加减速控制工程源码(梯形加减速算法)
网络·stm32·算法
一叶知秋yyds2 小时前
Ubuntu 虚拟机安装 OpenClaw 完整流程
linux·运维·ubuntu·openclaw
瀚高PG实验室2 小时前
审计策略修改
网络·数据库·瀚高数据库
forAllforMe3 小时前
etherCAT的协议VoE,FoE,EoE,CoE的概念和区别
网络
斯普信云原生组3 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器
大数据新鸟3 小时前
操作系统之虚拟内存
java·服务器·网络