有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

TLS连接中ServerHelloDone之后的java SocketTimeoutException

我有一个使用SSLServerSocket制作的服务器。服务器接收来自许多其他服务器的连接。其他服务器来自第三方。它总是适用于所有服务器,除了其中一台。典型的ssl调试日志如下所示:

WorkerThread-2, READ: Unknown-3.3 Handshake, length = 116
*** ClientHello, Unknown-3.3
RandomCookie:  GMT: 1513485218 bytes = { 151, 69, 77, 255, 242, 138, 61, 245, 71, 237, 98, 49, 92, 122, 152, 21, 229, 164, 150, 171, 11, 177, 238, 234, 63, 168, 90, 151 }
Session ID:  {}
Cipher Suites: [Unknown 0xc0:0x28, Unknown 0xc0:0x27, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, Unknown 0x0:0x3d, Unknown 0x0:0x3c, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp384r1, secp256r1}
Extension ec_point_formats, formats: [uncompressed]
Unsupported extension signature_algorithms, data: 00:12:06:01:06:03:04:01:05:01:02:01:04:03:05:03:02:03:02:02
Unsupported extension type_35, data:
Unsupported extension type_23, data:
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Created:  [Session-180, TLS_RSA_WITH_AES_128_CBC_SHA]
*** ServerHello, TLSv1
RandomCookie:  GMT: 1513485220 bytes = { 74, 28, 71, 12, 232, 178, 237, 132, 60, 224, 123, 53, 189, 12, 182, 240, 206, 94, 159, 96, 89, 29, 71, 144, 161, 254, 84, 32 }
Session ID:  {90, 54, 244, 164, 39, 152, 221, 223, 132, 77, 169, 99, 15, 202, 26, 191, 213, 70, 91, 125, 141, 91, 159, 248, 11, 145, 254, 187, 97, 178, 14, 233}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
Cipher suite:  TLS_RSA_WITH_AES_128_CBC_SHA
*** Certificate chain
      [... certificate chain follows ...]
***
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=QuoVadis Root CA 2 G3, O=QuoVadis Limited, C=BM>
<CN=DigiCert Assured ID Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US>
    .... many more cert authorities
WorkerThread-2, WRITE: TLSv1 Handshake, length = 16384
*** ServerHelloDone
WorkerThread-2, WRITE: TLSv1 Handshake, length = 1403
WorkerThread-2, READ: TLSv1 Handshake, length = 3983
*** Certificate chain
chain [0] = [
    ... client certificate chain follows
...
    ... connection proceeds normally and suceeds

正如您所看到的,握手似乎很正常,服务器通过证书请求客户端进行身份验证,然后客户端发送证书。但是,对于其中一个第三方服务器(我无权访问),握手过程如下所示:

WorkerThread-2, READ: TLSv1 Handshake, length = 159
*** ClientHello, Unknown-3.3
RandomCookie:  GMT: -750761141 bytes = { 238, 28, 230, 74, 9, 73, 28, 198, 222, 183, 234, 204, 37, 117, 50, 44, 71, 133, 93, 240, 66, 157, 241, 152, 75, 168, 0, 174 }
Session ID:  {}
Cipher Suites: [Unknown 0xc0:0x2b, Unknown 0xc0:0x2f, Unknown 0xcc:0xa9, Unknown 0xcc:0xa8, Unknown 0xc0:0x2c, Unknown 0xc0:0x30, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, Unknown 0x0:0x9c, Unknown 0x0:0x9d, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension renegotiation_info, renegotiated_connection: <empty>
Unsupported extension server_name, [host_name: smtp3.myserverdns.com]
Unsupported extension type_23, data:
Unsupported extension type_35, data:
Unsupported extension signature_algorithms, data: 00:12:04:03:08:04:04:01:05:03:08:05:05:01:08:06:06:01:02:01
Extension ec_point_formats, formats: [uncompressed]
Extension elliptic_curves, curve names: {unknown curve 29, secp256r1, secp384r1}
***
%% Created:  [Session-189, TLS_RSA_WITH_AES_128_CBC_SHA]
*** ServerHello, TLSv1
RandomCookie:  GMT: 1513485240 bytes = { 91, 241, 167, 85, 144, 140, 202, 57, 192, 1, 43, 95, 77, 164, 68, 210, 170, 37, 114, 50, 237, 255, 17, 205, 131, 74, 242, 21 }
Session ID:  {90, 54, 244, 184, 141, 168, 116, 151, 23, 155, 13, 108, 239, 23, 28, 117, 51, 182, 85, 174, 138, 132, 254, 29, 235, 231, 30, 184, 40, 27, 38, 145}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
Cipher suite:  TLS_RSA_WITH_AES_128_CBC_SHA
*** Certificate chain
chain [0] = [
[
  [... certificate chain follows ...]
***
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=QuoVadis Root CA 2 G3, O=QuoVadis Limited, C=BM>
<CN=DigiCert Assured ID Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US>
    .... many more cert authorities
WorkerThread-2, WRITE: TLSv1 Handshake, length = 16384
*** ServerHelloDone
WorkerThread-2, WRITE: TLSv1 Handshake, length = 1403
WorkerThread-2, handling exception: java.net.SocketTimeoutException: Read timed out
WorkerThread-2, called close()
WorkerThread-2, called closeInternal(true)
WorkerThread-2, SEND TLSv1 ALERT:  warning, description = close_notify
WorkerThread-2, WRITE: TLSv1 Alert, length = 2
WorkerThread-2, called close()
WorkerThread-2, called closeInternal(true)
WorkerThread-2, called close()
WorkerThread-2, called closeInternal(true)

如您所见,在ServerHelloDone之后,客户机没有响应,socket超时发生。事实上,超时总是在大约1.5秒后。握手开始后

为什么插座在这么短的时间后就超时了?我认为服务器hello中的某些内容对客户端来说不好,但我不知道它可能是什么。您是否发现两个ssl调试日志之间有任何显著差异,可以证明失败的合理性?我已经在网上搜索了好几个小时,但运气不好


共 (1) 个答案

  1. # 1 楼答案

    梅塔:不是答案,但评论太长了;我将在几天后删除

    在许多协议中,传输单元的“飞行”是一组一起发送的多个单元。在SSL/TLS握手协议中,客户机发送一条消息(ClientHello),然后服务器发送多条消息,这是它的第一次航班,由于客户机的航班很小(一条消息),我只需将服务器的第一次航班标记为第一次航班。它由ServerHello always、Certificate always、ServerKeyExchange有时、CertificateRequest Narrow和ServerHelloDone always组成。在此之后,客户机通常会发送多条消息的第二个航班,然后服务器会发送第二个航班,同上。参见rfc5246 (scroll down)第36页的图表或早期版本的等效图

    很明显,这第一次飞行的某些方面造成了问题;原因可能是it中的消息之一,或者是由单个TLS记录或几个连续记录组成的较低级别(TCP)传输;在您的情况下,每个日志行有两条记录

    ... WRITE: TLSv1 Handshake, length = 16384
    ... WRITE: TLSv1 Handshake, length = 1403
    

    TLS握手信息可以打包到记录中,但TLS记录仅限于16384条,并且您的第一次航班的合并信息超过了这一限制,因此需要两条记录。查看TCP行为的踪迹(通常仅在“有线”(物理或逻辑网络)级别可见)可以帮助我们区分

    TCP是一个太复杂的协议,如果你还不知道的话,我无法在这里解释。可能有数百本关于“TCP/IP”协议家族或“堆栈”的书,也可能有数百万个网站。对我来说wikipedia是相当严格和彻底的,但仍然不是非常困难。但简而言之,一旦建立了联系,当一个端点发送一些数据时(在一个或多个段大小的数据报中,在这种情况下不一定匹配上层数据单元TLS记录),如果另一个端点正确地接收到这些数据,它通常会使用TCP报头进行响应,在该报头中,“ACK”值的增量超过了传输数据中的“SEQ”值的范围数据如果我们确实看到了这样一个ACK值,那么我们就知道传输的数据已发送到远程系统上的TLS端点,如果您看到整个航班的ACK,我们就知道航班已发送,任何随后的问题都是远程端点处理消息的结果

    如果我们没有看到这样的确认,这意味着远程主机没有收到数据段;在过去,这有时意味着由于链接错误或延迟而丢失,但在链接几乎总是可靠和快速的现代互联网上,这几乎总是意味着数据被丢弃。特别是,如果您的系统、远程系统或其中任何网络链路之间的“MTU”大小(即“最大传输单元”)不匹配,可能会导致丢弃数据报,从而导致传输失败。TCP有一种旨在纠正这种情况的机制,称为PMTU(路径MTU)发现,但它依赖于ICMP。在现代互联网中,许多人屏蔽ICMP,因为他们通常认为这是一个安全问题,但并不总是错误的

    Wireshark(在Windows、Mac和一些Linux/Unix上)是一个(非常)完整的基于GUI的工具,用于捕获和解码网络数据tcpdump是一个更基本的命令行工具,在一些Unix上用于相同的目的,通常包括Linux;其他一些Unix也有类似的工具,有不同的名称和不同的细节。你没有提到你正在使用什么操作系统,所以我没有具体说明

    tcpdump默认情况下,对IP和TCP报头进行解码,因此您只需查看为每个line=数据包显示的seq和ack值,记住每个行在IP级别的方向;如果您的主机和网络上同时有其他活动,tcpdump可以选择过滤捕获和显示哪些数据包,使其更易于阅读tcpdump还可以选择捕获到一个或多个文件(通常称为something.pcap),而不是显示。Wireshark通常会为每个帧解码多个协议层,因此您需要在数据包详细信息窗格中查看每个数据包的“Internet协议”和“传输控制协议”层;它可以保存到捕获文件和显示文件,并且在捕获和显示时都有(不同的)过滤选项。如果你自己无法理解跟踪,那么在Q中输入tcpdump或等效文本输出可能就足够了,但可能需要在可访问的地方放置一个二进制pcap文件