【问题标题】:Curl is not sending client certificateCurl 未发送客户端证书
【发布时间】:2020-08-07 14:24:17
【问题描述】:

我正在尝试发送一个简单的 curl 请求:

curl -k -i --key ./key.pem --cert ./cert.pem https://target_ip/whatever/

我遇到的问题是它没有发送任何证书。验证显然通过了,否则我会收到诸如密钥不匹配之类的错误,但随后我可以在 Wireshark 中看到证书没有在客户端 Hello 周围的 TCP 连接中发送。像--verbose--cacert 这样的开关也没有多大作用。

我可以通过邮递员成功发送相同的证书。

我尝试从各种来源发送相同的 curl 请求,例如我的 WSL2 ubuntu、云中的 debian 容器、VM……

任何提示为什么它不发送证书?

EDIT I - curl -v 的输出

*   Trying 52.xxx.xxx.xx:443...
* TCP_NODELAY set
* Connected to 52.xxx.xxx.xx (52.xxx.xxx.xx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=NGINXIngressController
*  start date: Aug 10 18:08:13 2020 GMT
*  expire date: Aug 10 18:08:13 2021 GMT
*  issuer: CN=NGINXIngressController
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET /whatever/ HTTP/1.1
> Host: custom.localhost.dev
> User-Agent: curl/7.68.0
> Accept: */*
> Authorization: Bearer  eyJ0...
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
HTTP/1.1 403 Forbidden
< Server: nginx/1.19.0
Server: nginx/1.19.0
< Date: Mon, 10 Aug 2020 22:23:24 GMT
Date: Mon, 10 Aug 2020 22:23:24 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 153
Content-Length: 153
< Connection: keep-alive
Connection: keep-alive

<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.0</center>
</body>
</html>
* Connection #0 to host 52.xxx.xxx.xx left intact

EDIT II - wireshark 捕获

匿名 pcap 似乎太麻烦了,所以这里只是一些快照。希望你能看到你需要的一切。我已经突出显示了我确实(没有)看到正在发送的证书的数据包。请注意,我在 Windows 工作站上运行邮递员,而 curl 在 WSL2 中,因此源地址不同。 curl 的其他主机的行为确实相同。

卷曲

邮递员

EDIT III - 客户问候

卷曲

邮递员

【问题讨论】:

  • 您为什么使用不安全模式的-k - 还有与密钥或证书关联的密码吗?
  • "...我可以在 Wireshark 中看到 ..." - 您在 Wireshark 中看到服务器实际上请求证书(即@9​​87654334@)吗? URL与邮递员完全相同吗?你能提供来自curl -v的调试输出吗?
  • @user3788685 服务器有自签名证书。我不知道如何将它提供给 curl,因为 --cacert 似乎没有解决这个问题。因此-k。 @SteffenUllrich 不,不需要证书,因为它在 nginx 服务器 (ssl_verify_client optional_no_ca;) 上纯粹是可选的。是的,网址完全一样。我将在已编辑的问题中提供 -v 输出。
  • @Trimack: "...不请求证书,因为它在 nginx 服务器上完全是可选的 (ssl_verify_client optional_no_ca;)。" - 使用此设置,证书是仍然请求,但接受一个空证书作为响应。一般来说,除非明确要求,否则客户端永远不会发送证书。
  • 详细输出清楚地显示没有来自服务器的 CertificateRequest。这就是客户端不发送任何客户端证书的原因。能否请您提供一个从失败的 curl 连接和成功的邮递员连接中捕获的数据包,以便可以看到有什么区别?

标签: ssl curl networking client-certificates


【解决方案1】:

ClientHello 显示了一个明显的区别:postman 使用 server_name 扩展 (SNI) 来提供预期的主机名,而 curl 没有。

这可能会触发 web 服务器中配置的不同部分:postman 触发对指定为 server_name 的特定虚拟主机的访问,而 curl 可能会运行到默认配置。假设只有特定的虚拟主机启用客户端证书,这就解释了为什么 CertificateRequest 仅由服务器发送给邮递员而不是 curl。

不清楚这个主机名是什么,但根据长度,它不可能是 IP 地址。因此,邮递员必须以某种方式知道服务器的预期主机名,即使声称访问是仅使用https://target_ip/ 完成的,即没有给定的主机名。 curl 无法从此 URL 派生预期的主机名,因此无法设置 server_name。要让 curl 知道要设置 server_name 的主机名,同时仍然能够访问特定 IP,请使用 --resolve 选项:

curl --resolve hostname:443:target_ip https://hostname/

【讨论】:

  • 非常感谢。你说对了。只是在这里提供进一步的解释:当 Client Hello 进来时,Nginx 必须选择一个服务器块和证书来颁发。在这一点上,它还没有Host 标头,显然是根据 Postman 神奇填充而 curl 没有的 SNI 来决定的。因此,对于 TLS 握手,配置(服务器块)不同于基于标头处理 HTTP 的配置。
  • @Trimack: 是的,这就是 SNI 的全部意义——在 TLS 握手中已经有了目标名称,以便在有多个名称时可以根据请求的名称选择配置(包括证书)相同的 IP 地址。
猜你喜欢
  • 2013-06-06
  • 2014-04-07
  • 1970-01-01
  • 2014-04-15
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多