当服务器验证客户端证书失败时,TLS 1.3 客户端不会报告握手失败

Dav*_*ter 6 ssl openssl tls1.3

我有一个使用 OpenSSL 的 C 客户端,当使用在服务器上的 SSL_do_handshake() 调用期间在服务器端验证失败的证书时,该客户端测试失败。当应用程序使用 TLS 1.2 时,当服务器调用 SSL_do_handshake() 作为失败返回值时,服务器上的 SSL_do_handshake() 失败将报告给客户端。

将我的应用程序升级到 OpenSSL 1.1.1 和 TLS 1.3 时,我注意到虽然服务器上仍然发生验证错误,但不再将其报告回客户端。

我知道握手协议作为 TLS 1.3 的一部分被完全重写,但似乎有了所有可用的回调,我应该能够在客户端以某种方式确定身份验证失败,而不必尝试编写数据到服务器。

还有其他人遇到过这种情况吗?他们可以推荐一条前进的道路吗?

Mat*_*ell 7

当 TLSv1.2 和 TLSv1.3 中的服务器和客户端都写入“完成”消息并从对等方收到消息时,它们就认为握手已完成。这是 TLSv1.2 中握手的样子(取自 RFC5246):

      Client                                               Server

      ClientHello                  -------->
                                                      ServerHello
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*
      [ChangeCipherSpec]
      Finished                     -------->
                                               [ChangeCipherSpec]
                                   <--------             Finished
      Application Data             <------->     Application Data
Run Code Online (Sandbox Code Playgroud)

因此,在这里您可以看到客户端在与服务器的第二次通信中发送其证书和完成消息。然后,它等待接收从服务器返回的 ChangeCipherSpec 和 Finished 消息,然后才认为握手“完成”并可以开始发送应用程序数据。

这是取自 RFC8446 的 TLSv1.3 的等效流程:

       Client                                           Server

Key  ^ ClientHello
Exch | + key_share*
     | + signature_algorithms*
     | + psk_key_exchange_modes*
     v + pre_shared_key*       -------->
                                                  ServerHello  ^ Key
                                                 + key_share*  | Exch
                                            + pre_shared_key*  v
                                        {EncryptedExtensions}  ^  Server
                                        {CertificateRequest*}  v  Params
                                               {Certificate*}  ^
                                         {CertificateVerify*}  | Auth
                                                   {Finished}  v
                               <--------  [Application Data*]
     ^ {Certificate*}
Auth | {CertificateVerify*}
     v {Finished}              -------->
       [Application Data]      <------->  [Application Data]
Run Code Online (Sandbox Code Playgroud)

TLSv1.3 的优点之一是它加快了完成握手所需的时间。在 TLSv1.3 中,客户端会先从服务器接收“已完成”消息,然后再发回其证书和已完成消息。当客户端发送“Finished”消息时,它已经收到“Finished”消息,因此握手已完成,并且可以立即开始发送应用程序数据。

这当然意味着客户端在下次从服务器读取数据之前不会知道服务器是否接受了证书。如果它被拒绝,那么客户端将读取的下一个内容将是失败警报(否则它将是正常的应用程序数据)。

我知道握手协议作为 TLS 1.3 的一部分被完全重写,但似乎有了所有可用的回调,我应该能够在客户端以某种方式确定身份验证失败,而不必尝试编写数据到服务器。

重要的不是将数据写入服务器,而是读取数据。只有这样你才能知道服务器是否发送了警报或只是正常的应用程序数据。在读取该数据之前,OpenSSL 中没有可用的回调来告诉您这一点 - 因为 OpenSSL 本身由于底层协议而无法知道。