curl 与客户端证书身份验证

Mas*_*imo 5 authentication ssl curl http ssl-certificate

我们喜欢使用客户端证书身份验证而不是基本身份验证来访问网络服务器。

证书是 PEM 证书,密钥文件是单独的文件。

curl 调用如下所示:

curl -v --cert cert.crt --key key.key --pass foobar https://testserver/soap/request
Run Code Online (Sandbox Code Playgroud)

从调试日志:

HTTP/1.1 401 Unauthorized
X-message-code: PWD_WRONG
WWW-Authenticate: Basic realm="Test Platform"
Transfer-Encoding: chunked
Date: Wed, 30 May 2018 10:26:51 GMT
Server: TEST
Set-Cookie: ...
Run Code Online (Sandbox Code Playgroud)

我了解到 WWW-Authenticate: Basic 是从服务器到客户端提供基本身份验证的请求。但是对于未经身份验证的请求可能会发生这种情况,因此它不能证明服务器不提供证书身份验证。

阅读https://medium.com/@sevcsik/authentication-using-https-client-certificates-3c9d270e8326 ...作为双重检查,我尝试使用 Firefox 浏览器将两个文件合并为 PKCS#12 格式并将其导入浏览器。它可以工作,因此服务器支持证书身份验证。

失败的 curl 调用有什么问题?是否有任何支持证书认证的测试服务器?https://www.httpbin.org/似乎没有提供该设置。

使用带有 curl 的 PKCS#12 版本时会发生相同的错误,如下所示:

curl -v --cert cert.p12 --cert-type p12 --pass foobar
https://testserver/soap/request...
Run Code Online (Sandbox Code Playgroud)

完整的调试日志:

* Uses proxy env variable no_proxy == '10.0.0.0/8,127.0.0.1,172.16.0.0/12,192.168.20.0/24,'
* Uses proxy env variable https_proxy == 'http://gateway01:8080'
*   Trying 10.190.224.23...
* TCP_NODELAY set
* Connected to gateway01 (10.190.224.23) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to testserver:443
> CONNECT testserver:443 HTTP/1.1
> Host: testserver:443
> User-Agent: curl/7.60.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 Connection established
< 
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (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, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=DE; ST=Baden-Wurttemberg; L=...; O=...; OU=...; CN=...
*  start date: Aug 15 07:54:38 2016 GMT
*  expire date: Aug 15 07:54:38 2019 GMT
*  subjectAltName: host "testserver" matched cert's "*.testserver"
*  issuer: C=NL; L=Amsterdam; O=Verizon Enterprise Solutions; OU=Cybertrust; CN=Verizon Public SureServer CA G14-SHA2
*  SSL certificate verify ok.
> GET /soap/request HTTP/1.1
> Host: testserver
> User-Agent: curl/7.60.0
> Accept: */*
> 
< HTTP/1.1 401 Unauthorized
< X-message-code: PWD_WRONG
< WWW-Authenticate: Basic realm="Test Platform"
< Transfer-Encoding: chunked
< Date: Wed, 06 Jun 2018 05:10:56 GMT
< Server: TEST
< Set-Cookie: ...; path=/; httponly; secure
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< 
* Connection #0 to host gateway01 left intact
Run Code Online (Sandbox Code Playgroud)

由于 Firefox 能够进行身份验证,我使用 Wireshark 分析了这两个请求。我通过在两个网络转储中过滤“tcp contains”和我的证书的已知 CN 来找到证书数据,但 Firefox 和 curl 将证书放在不同的数据包上。在 localhost:8081 上有一个 SOCKS5 代理,我捕获环回接口并在该 tcp 端口 8081 上进行过滤,您可以在转储中看到:

火狐:

$ tshark -r firefox_request.pcapng
    1 0.000000000    127.0.0.1 ? 127.0.0.1    TCP 74 51254 ? 8081 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1945282596 TSecr=0 WS=128
    2 0.000013339    127.0.0.1 ? 127.0.0.1    TCP 74 8081 ? 51254 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1945282596 TSecr=1945282596 WS=128
    3 0.000025766    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=1945282597 TSecr=1945282596
    4 0.000052377    127.0.0.1 ? 127.0.0.1    TCP 69 51254 ? 8081 [PSH, ACK] Seq=1 Ack=1 Win=43776 Len=3 TSval=1945282597 TSecr=1945282596
    5 0.000058018    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51254 [ACK] Seq=1 Ack=4 Win=43776 Len=0 TSval=1945282597 TSecr=1945282597
    6 0.000110299    127.0.0.1 ? 127.0.0.1    TCP 68 8081 ? 51254 [PSH, ACK] Seq=1 Ack=4 Win=43776 Len=2 TSval=1945282597 TSecr=1945282597
    7 0.000127024    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=4 Ack=3 Win=43776 Len=0 TSval=1945282597 TSecr=1945282597
    8 0.000145980    127.0.0.1 ? 127.0.0.1    TCP 112 51254 ? 8081 [PSH, ACK] Seq=4 Ack=3 Win=43776 Len=46 TSval=1945282597 TSecr=1945282597 [TCP segment of a reassembled PDU]
    9 0.049958725    127.0.0.1 ? 127.0.0.1    TCP 76 8081 ? 51254 [PSH, ACK] Seq=3 Ack=50 Win=43776 Len=10 TSval=1945282646 TSecr=1945282597
   10 0.050239394    127.0.0.1 ? 127.0.0.1    TCP 583 51254 ? 8081 [PSH, ACK] Seq=50 Ack=13 Win=43776 Len=517 TSval=1945282647 TSecr=1945282646 [TCP segment of a reassembled PDU]
   11 0.100158230    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51254 [ACK] Seq=13 Ack=567 Win=44800 Len=0 TSval=1945282697 TSecr=1945282647
   12 0.107621822    127.0.0.1 ? 127.0.0.1    TLSv1.2 2786 Server Hello
   13 0.107672270    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=567 Ack=2733 Win=174720 Len=0 TSval=1945282704 TSecr=1945282704
   14 0.107832838    127.0.0.1 ? 127.0.0.1    TLSv1.2 1426 Certificate, Server Key Exchange
   15 0.108417953    127.0.0.1 ? 127.0.0.1    TCP 1426 8081 ? 51254 [PSH, ACK] Seq=4093 Ack=567 Win=44800 Len=1360 TSval=1945282705 TSecr=1945282704 [TCP segment of a reassembled PDU]
   16 0.108423407    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=567 Ack=5453 Win=436608 Len=0 TSval=1945282705 TSecr=1945282704
   17 0.109263897    127.0.0.1 ? 127.0.0.1    TLSv1.2 2674 Certificate Request, Server Hello Done
   18 0.118830308    127.0.0.1 ? 127.0.0.1    TCP 3613 51254 ? 8081 [PSH, ACK] Seq=567 Ack=8061 Win=464896 Len=3547 TSval=1945282715 TSecr=1945282706 [TCP segment of a reassembled PDU]
   19 0.118851470    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51254 [ACK] Seq=8061 Ack=4114 Win=175744 Len=0 TSval=1945282715 TSecr=1945282715
   20 0.173415105    127.0.0.1 ? 127.0.0.1    TLSv1.2 117 Change Cipher Spec, Client Hello[Malformed Packet]
   21 0.173932334    127.0.0.1 ? 127.0.0.1    TCP 798 51254 ? 8081 [PSH, ACK] Seq=4114 Ack=8112 Win=464896 Len=732 TSval=1945282770 TSecr=1945282770 [TCP segment of a reassembled PDU]
   22 0.173943923    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51254 [ACK] Seq=8112 Ack=4846 Win=182912 Len=0 TSval=1945282770 TSecr=1945282770
   23 0.250413758    127.0.0.1 ? 127.0.0.1    TLSv1.2 844 Application Data
   24 0.300115138    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=4846 Ack=8890 Win=464896 Len=0 TSval=1945282897 TSecr=1945282847
   25 2.490166002    127.0.0.1 ? 127.0.0.1    TCP 798 51254 ? 8081 [PSH, ACK] Seq=4846 Ack=8890 Win=464896 Len=732 TSval=1945285087 TSecr=1945282847 [TCP segment of a reassembled PDU]
   26 2.490201268    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51254 [ACK] Seq=8890 Ack=5578 Win=184320 Len=0 TSval=1945285087 TSecr=1945285087
   27 2.554148656    127.0.0.1 ? 127.0.0.1    TLSv1.2 844 Application Data
   28 2.554155780    127.0.0.1 ? 127.0.0.1    TCP 66 51254 ? 8081 [ACK] Seq=5578 Ack=9668 Win=464896 Len=0 TSval=1945285151 TSecr=1945285151
   29 12.610141935    127.0.0.1 ? 127.0.0.1    TCP 66 [TCP Keep-Alive] 51254 ? 8081 [ACK] Seq=5577 Ack=9668 Win=464896 Len=0 TSval=1945295207 TSecr=1945285151
   30 12.610162448    127.0.0.1 ? 127.0.0.1    TCP 66 [TCP Keep-Alive ACK] 8081 ? 51254 [ACK] Seq=9668 Ack=5578 Win=184320 Len=0 TSval=1945295207 TSecr=1945285151
Run Code Online (Sandbox Code Playgroud)

卷曲:

$ tshark -r curl_request.pcapng
    1 0.000000000    127.0.0.1 ? 127.0.0.1    TCP 74 51196 ? 8081 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1944382941 TSecr=0 WS=128
    2 0.000014517    127.0.0.1 ? 127.0.0.1    TCP 74 8081 ? 51196 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1944382941 TSecr=1944382941 WS=128
    3 0.000026917    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=1944382941 TSecr=1944382941
    4 0.000068294    127.0.0.1 ? 127.0.0.1    TCP 69 51196 ? 8081 [PSH, ACK] Seq=1 Ack=1 Win=43776 Len=3 TSval=1944382941 TSecr=1944382941
    5 0.000076382    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=1 Ack=4 Win=43776 Len=0 TSval=1944382941 TSecr=1944382941
    6 0.000105767    127.0.0.1 ? 127.0.0.1    TCP 68 8081 ? 51196 [PSH, ACK] Seq=1 Ack=4 Win=43776 Len=2 TSval=1944382941 TSecr=1944382941
    7 0.000114276    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=4 Ack=3 Win=43776 Len=0 TSval=1944382941 TSecr=1944382941
    8 0.012753479    127.0.0.1 ? 127.0.0.1    TCP 76 51196 ? 8081 [PSH, ACK] Seq=4 Ack=3 Win=43776 Len=10 TSval=1944382954 TSecr=1944382941
    9 0.056073156    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=3 Ack=14 Win=43776 Len=0 TSval=1944382997 TSecr=1944382954
   10 0.060558849    127.0.0.1 ? 127.0.0.1    TCP 76 8081 ? 51196 [PSH, ACK] Seq=3 Ack=14 Win=43776 Len=10 TSval=1944383002 TSecr=1944382954
   11 0.106077184    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=14 Ack=13 Win=43776 Len=0 TSval=1944383047 TSecr=1944383002
   12 0.156827822    127.0.0.1 ? 127.0.0.1    TLSv1 583 Client Hello
   13 0.156838865    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=13 Ack=531 Win=44800 Len=0 TSval=1944383098 TSecr=1944383098
   14 0.204829368    127.0.0.1 ? 127.0.0.1    TLSv1.2 2786 Server Hello
   15 0.204837221    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=531 Ack=2733 Win=174720 Len=0 TSval=1944383146 TSecr=1944383146
   16 0.204942723    127.0.0.1 ? 127.0.0.1    TLSv1.2 4146 Certificate, Server Key Exchange
   17 0.204950957    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=531 Ack=6813 Win=305664 Len=0 TSval=1944383146 TSecr=1944383146
   18 0.204981363    127.0.0.1 ? 127.0.0.1    TLSv1.2 1314 Certificate Request, Server Hello Done
   19 0.204987399    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=531 Ack=8061 Win=436608 Len=0 TSval=1944383146 TSecr=1944383146
   20 0.208331980    127.0.0.1 ? 127.0.0.1    TLSv1.2 2066 Certificate, Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message
   21 0.208341083    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=8061 Ack=2531 Win=175744 Len=0 TSval=1944383149 TSecr=1944383149
   22 0.255529146    127.0.0.1 ? 127.0.0.1    TLSv1.2 117 Change Cipher Spec, Client Hello[Malformed Packet]
   23 0.255792019    127.0.0.1 ? 127.0.0.1    TLSv1.2 220 Application Data
   24 0.255799639    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=8112 Ack=2685 Win=179840 Len=0 TSval=1944383197 TSecr=1944383197
   25 2.312629664    127.0.0.1 ? 127.0.0.1    TLSv1.2 535 Application Data
   26 2.312809015    127.0.0.1 ? 127.0.0.1    TLSv1.2 97 Encrypted Alert
   27 2.312821190    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=8581 Ack=2716 Win=179840 Len=0 TSval=1944385254 TSecr=1944385254
   28 2.313444679    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [FIN, ACK] Seq=2716 Ack=8581 Win=444800 Len=0 TSval=1944385255 TSecr=1944385254
   29 2.356079624    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [ACK] Seq=8581 Ack=2717 Win=179840 Len=0 TSval=1944385297 TSecr=1944385255
   30 2.357676736    127.0.0.1 ? 127.0.0.1    TCP 66 8081 ? 51196 [FIN, ACK] Seq=8581 Ack=2717 Win=179840 Len=0 TSval=1944385299 TSecr=1944385255
   31 2.357690888    127.0.0.1 ? 127.0.0.1    TCP 66 51196 ? 8081 [ACK] Seq=2717 Ack=8582 Win=444800 Len=0 TSval=1944385299 TSecr=1944385299
Run Code Online (Sandbox Code Playgroud)

过滤本地证书的特殊 CN 我可以在两个转储中找到它:

$ for file in {firefox,curl}_request.pcapng;do tshark -r "$file" -Y "tcp contains FOOBAR";done
   18 0.118830308    127.0.0.1 ? 127.0.0.1    TCP 3613 51254 ? 8081 [PSH, ACK] Seq=567 Ack=8061 Win=464896 Len=3547 TSval=1945282715 TSecr=1945282706 [TCP segment of a reassembled PDU]
   20 0.208331980    127.0.0.1 ? 127.0.0.1    TLSv1.2 2066 Certificate, Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message
Run Code Online (Sandbox Code Playgroud)

但是,它仅作为 curl 的 TLS 握手的一部分被检测到:

$ for file in {firefox,curl}_request.pcapng;do tshark -r "$file" -Y "ssl contains FOOBAR";done
   20 0.208331980    127.0.0.1 ? 127.0.0.1    TLSv1.2 2066 Certificate, Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message
Run Code Online (Sandbox Code Playgroud)

Firefox(成功)和Curl(失败)的认证有什么不同?

Lek*_*eyn 7

服务器在其CertificateRequest 消息中包含可接受的证书颁发机构的列表。然后,客户端应发送根据这些标准可接受的证书链。

基于您的客户端证书包含在 Firefox 中的“重组 PDU 的 TCP 段”中这一事实,我猜测它还包含中间证书颁发机构来完成认证路径。这会放大握手消息,使其不再适合单个 TLS 记录(或 TCP 段)。

使用 cURL,您可能没有包含此中间证书,这会导致从客户端到服务器的证书消息较小,适合单个 TLS 记录。这会导致 Wireshark 显示ssl contains YOUR_CN. (此显示问题的原因实际上是 Wireshark 中缺少的功能,跨不同 TLS 记录重新组装握手消息,错误 3303。

要解决此问题,您可以尝试将中间 CA 附加到本地客户端证书。

(我可能是错的,但这是我仅根据文本跟踪就能想到的可能原因之一。)


Mas*_*imo 3

谢谢 Lekensteyn,你是对的,这是关于 CA 中级证书的。查看证书(Firefox 的客户端密钥交换),它添加了来自 Comodo 的另一个中间 CA,我只是不知道它从哪里获得。

然而,将 2 个证书合并到一个文件中对curl 没有帮助,也没有帮助添加 2 个 --cert。但是 --cacert 起作用了,这很有趣,因为 --cacert 实际上是为了设置 CA 证书来检查客户端的服务器证书。