2017-12-18 5 views
0

SSLServerSocket을 사용하여 서버를 만들었습니다. 서버는 많은 다른 서버로부터 연결을받습니다. 다른 서버는 제 3 자의 서버입니다. 그 중 하나를 제외하고 항상 모든 서버에서 잘 작동합니다. 전형적인 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 디버그 로그 사이에 중요한 차이점이 있습니까? 나는 운이없는 몇 시간 동안 인터넷에서 검색을 해왔다.

+0

왜 1.5 초의 읽기 시간 초과를 설정하고 있습니까? – EJP

+0

제한 시간 값을 설정하지 않고 있으며, 일반적으로 1.5 초 후에 시간 초과됩니다. 사용 된 시간 초과 값은 JVM 또는 시스템 기본값입니다. 이 문제는 한 클라이언트에서만 발생하며 모든 연결에서 구성이 동일하다는 점에 유의하십시오. – joanlofe

+0

문자 그대로 메시지 접두어 만 포함하는 HelloDone은 아닙니다. 내가 생각할 수있는 모든 콘텐츠 관련 오류가 경고 또는 적어도 FIN 또는 RST를 생성해야하거나 처음 비행 자체 일 수는 있지만 고객이 응답하기 전에 모두 함께 전송되는 첫 번째 비행의 다른 것일 수 있습니다 . 가능한 경우 wireshark tcpdump 또는 유사한 방법으로 유선 추적을 가져 와서 첫 번째 비행에 대해 TCP 수준 확인을 받고 있는지 확인하십시오. 그렇다면 클라이언트 시스템을 보지 않고 진행할 수 없습니다. 클라이언트 시스템과 클라이언트 시스템 사이의 네트워크 경로를 살펴 보지 않으십시오. –

답변

0

메타 : 대답은 아니지만 의견이 너무 길다. 나는 며칠 후에 삭제할 것이다.

많은 프로토콜에서 전송 단위의 '비행'은 함께 전송되는 여러 단위의 그룹입니다. SSL/TLS 핸드 셰이크 프로토콜에서 클라이언트는 하나의 메시지 (ClientHello)를 보내고 서버는 첫 번째 비행 인 여러 개의 메시지를 보내고 클라이언트의 비행은 간단합니다 (하나의 메시지) 간단히 서버의 첫 번째 비행에 첫 번째 비행이라는 레이블을 붙입니다. ServerHello always, Certificate 보통, ServerKeyExchange 때때로, CertificateRequest는 거의, ServerHelloDone은 항상 구성됩니다. 이 후 보통 클라이언트는 여러 메시지의 두 번째 비행을 보낸 다음 서버에 두 번째 비행을 보냅니다. 이전 버전에서는 rfc5246 (scroll down) 또는 그와 동등한 36 페이지의 도표를 참조하십시오.

분명히이 첫 비행에 관한 것이 문제를 일으켰습니다. 원인은 메시지 중 하나 또는 단일 TLS 레코드 또는 몇 개의 인접한 메시지로 구성된 하위 수준 (TCP) 전송 중 하나 일 수 있습니다. 귀하의 경우는 로그 라인

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

TLS 핸드 셰이크 메시지 기록에 포장 할 수 있지만 TLS 레코드를 16384로 제한하고 첫 비행의 결합 된 메시지는 그래서 두 개의 레코드를 필요로 초과 당, 두 개의 레코드입니다. 일반적으로 '와이어'(물리적 또는 논리적 네트워크) 수준에서만 볼 수있는 TCP 동작의 흔적을 보면 우리가 구별하는 데 도움이 될 수 있습니다.

TCP가 너무 복잡하여 여기에 대해 설명 할 수 없습니다. 'TCP/IP'프로토콜 패밀리 또는 '스택'에 관한 책은 아마 수백 권이고, 아마도 수백만 개의 웹 사이트가있을 것입니다. 내 돈을 위해서 wikipedia은 상당히 엄격하고 철저하지만 여전히 정신없이 어려운 것은 아닙니다. 그러나 일단 연결이 설정되면 한 엔드 포인트가이 데이터를 올바르게 수신하면 하나의 엔드 포인트가 일부 데이터 (하나 이상의 세그먼트 크기의 데이터 그램에서,이 경우 TLS 레코드의 상위 레벨 데이터 단위와 반드시 일치하지는 않음)를 보낼 때 는 일반적으로 'ACK'값이 전송 된 데이터의 'SEQ'값의 범위를 넘어서서 증가 된 TCP 헤더로 응답합니다. 그런 ACK 값을 보면 전송 된 데이터가 원격 시스템의 TLS 엔드 포인트로 전달되었음을 알 수 있습니다. 전체 비행에 대한 ACK가 표시되면 비행이 전달되었음을 알게되고 즉시 발생하는 문제는 원격 엔드 포인트가 메시지를 처리합니다.

OTOH 이러한 ACK가 표시되지 않으면 원격 호스트가 데이터 세그먼트를받지 못했음을 의미합니다. 과거에는 링크 오류 또는 지연으로 인한 손실이 있었지만 대부분의 경우 링크가 거의 항상 안정적이고 빠른 최신 인터넷에서는 거의 항상 데이터가 이 (가)으로 삭제되었습니다. 특히 시스템, 원격 시스템 또는 'MTU'크기, 즉 '최대 전송 단위'사이의 네트워크 링크간에 불일치가있는 경우 데이터 그램이 삭제 될 수 있으므로 전송에 실패했습니다. TCP는 PMTU (path MTU) 발견이라고 불리는이를 해결하기위한 메커니즘을 가지고 있지만, ICMP에 의존하며, 현대 인터넷에서 많은 사람들이 ICMP를 차단한다고 생각하기 때문에 항상 잘못된 것은 아니지만 보안 문제입니다.

Wireshark (Windows, Mac 및 일부 Linux/Unixes)는 네트워크 데이터를 캡처하고 디코딩하는 GUI 기반 도구입니다. tcpdump은 리눅스를 포함하여 일부 유닉스에서 같은 목적의보다 기본적인 명령 행 도구입니다. 어떤 다른 유닉스는 다른 이름과 다른 세부 사항을 가진 비슷한 도구를 가지고있다. 당신은 어떤 OS를 사용하고 있는지 언급하지 않았으므로 나는 구체적이지 않았습니다.

tcpdump은 기본적으로 IP 및 TCP 헤더를 디코딩하므로 IP 수준에서 각각의 방향을 염두에두면 각 줄마다 패킷에 표시된 seq 및 ack 값을 간단하게 볼 수 있습니다. 호스트와 네트워크에서 동시에 다른 활동이있는 경우 tcpdump에는 캡처 및 표시되는 패킷을 필터링 할 수있는 옵션이있어 읽기가 쉬워집니다. tcpdump에는 표시하는 대신 파일 (일반적으로 something.pcap)을 캡처하는 옵션도 있습니다. Wireshark는 일반적으로 각 프레임에 대해 여러 프로토콜 계층을 디코딩하므로 각 패킷의 패킷 세부 정보 창에서 '인터넷 프로토콜'및 '전송 제어 프로토콜'계층을 확인해야합니다. 그것은 캡쳐 파일 뿐만 아니라 디스플레이를 저장할 수 있으며 캡쳐 및 디스플레이하는 동안 필터링 할 수있는 (다른) 옵션을 가지고 있습니다. 스스로 트레이스를 파악할 수 없다면 Q에 tcpdump 또는 이와 동등한 텍스트 출력을 넣는 것으로 충분할 수도 있지만 이진 pcap 파일을 어딘가에 액세스 할 수 있어야합니다.

+0

와우! 좋은 설명, 많이 고마워! 나는 wireshark를 사용하고 위의 로그가 아닌 재미있는 것을 발견했습니다. 예를 들어, 클라이언트가 SSL 메시지가 아닌 서버의 이전 메시지로 인해 발생했을 가능성이있는 protocol_version 유형의 치명적인 경고를 전송하고 있습니다. 이제 서버가 두 개의 SSL 메시지 사이에 비 SSL 메시지를 보내는 이유를 찾으려고합니다. 나는 나중에 wireshark 로그를 게시하려고 시도 할 것이다. – joanlofe