• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

在TLS连接中的ServerHelloDone之后的SocketTimeoutException

java 来源:joanlofe 7次浏览

我有使用SSLServerSocket制作的服务器。服务器接收来自许多其他服务器的连接。其他服务器来自第三方。除了其中一个服务器外,它一直适用于所有服务器。典型的SSL调试日志是这样的:在TLS连接中的ServerHelloDone之后的SocketTimeoutException

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) 

正如你所看到的,inmediately的ServerHelloDone后没有来自客户端的响应到达和插座超时发生。事实上,超时总是只有1.5秒后。握手开始后。

为什么插座在这么短的时间后超时?我认为服务器hello中的某些内容对客户端来说并不好,但我不知道它会是什么。你看到两个ssl调试日志之间的任何显着差异,证明失败?我一直在互联网上搜索几个小时,没有运气。


===========解决方案如下:

Meta:不是一个答案,但太长的评论;我会在几天内删除。

在许多协议中,传输单元的“飞行”是一起发送的几个单元的组。在SSL/TLS握手协议中,客户端发送一个消息(ClientHello),然后服务器发送多个消息,这是它的第一次飞行,并且由于客户端的飞行是微不足道的(一条消息),我简单地在服务器的第一次飞行中标记第一次飞行。它通常由ServerHello,Certificate通常,ServerKeyExchange,很少的CertificateRequest和ServerHelloDone组成。在这之后,通常客户端会发送第二个航班的几条消息,然后服务器再次飞行。请参阅rfc5246 (scroll down)的第36页上的图表或早期版本中的等效项。

很明显,第一次飞行造成了一个问题;原因可能是其中的它或由单个TLS记录或几个连续的记录组成的较低级(TCP)传输;在你的情况下,两个记录,每个日志行

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

TLS握手消息可以被包装成的记录,但TLS记录仅限于16384和你的第一次飞行的组合的消息超过了所以它需要两个记录。看一下通常仅在’wire’(物理或逻辑网络)级别可见的TCP行为的痕迹可以帮助我们区分。

TCP协议太复杂了,我可以在这里解释一下,如果你还不知道的话。可能有数百本关于’TCP/IP’协议族或’堆栈’的书籍,可能还有数百万个网站。为了我的钱wikipedia是相当严格和彻底的,但仍然不是非常困难。但是总之,一旦建立连接,当一个端点发送一些数据(在一个或多个段大小的数据报中,这些数据报不一定与上层数据单元匹配,在这种情况下,TLS记录),如果另一个端点正确接收到这些数据,通常会用一个TCP报头进行响应,其中’ACK’值已经递增超过传输数据中’SEQ’值的范围。如果我们确实看到了这样一个ACK值,那么我们知道发送的数据被传送到远程系统上的TLS端点,并且如果您看到整个航班的ACK,则我们知道该航班已交付,并且任何紧接着的后续问题都是远程端点处理消息。

OTOH如果我们没有看到这样的ACK,这意味着远程主机没有收到数据段;在过去这有时意味着由于链路错误或延迟造成的损失,但在现代互联网中,链路几乎总是可靠和快速的,它几乎总是意味着数据被丢弃。特别是如果您的系统,远程系统或任何网络链路与“MTU”大小(即“最大传输单位”)之间存在不匹配,则可能导致数据报被丢弃,因此传输失败。 TCP有一种旨在纠正这种情况的机制,称为PMTU(路径MTU)发现,但它依赖于ICMP,并且在现代互联网中,许多人阻止ICMP,因为他们认为,通常但并非总是错误的是安全问题。 Wireshark(在Windows,Mac和某些Linux/Unix上)是一个(非常)完整的基于GUI的工具,用于捕获和解码网络数据。 tcpdump是一些更为基本的命令行工具,用于某些Unix上的相同目的,通常包括Linux;一些其他的Unix有类似的工具,名称和细节不同。你没有提到你使用的操作系统,所以我没有具体说明。

tcpdump默认解码IP和TCP报头,因此您可以简单地查看每个line = packet显示的seq和ack值,并记住每个IP层的方向;如果您的主机和网络上同时存在其他活动,tcpdump可以选择过滤哪些数据包被捕获并显示,这使得阅读起来更容易。 tcpdump也有捕获到一个或多个文件(通常命名为something.pcap)而不是显示的选项。 Wireshark通常会为每个帧解码多个协议层,因此您需要查看每个数据包的数据包详细信息窗格中的“Internet协议”和“传输控制协议”层;它可以保存到捕获文件以及显示,并且具有(不同)选项以在捕获和显示时对两者进行过滤。如果您无法自己找出曲线,那么在您的Q中输入tcpdump或等效文本的输出可能就足够了,但可能需要将二进制pcap文件放在可访问的地方。


版权声明:本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。
喜欢 (0)