Обновление сокета до SSLSocket с помощью STARTTLS: recv не удалось

Я пытаюсь обновить сокет до SSLSocket, используя STARTTLS. На вики InspIRCd это должно работать так

>> STARTTLS
<< :test2.chatspike.net 670 nickname :STARTTLS successful, go ahead with TLS handshake
(SSL Handshake)

Итак, в моем коде я написал (немного упрощенно)

else if (code.equals("670")) {
    SSLSocketFactory sslSocketFactory = ((SSLSocketFactory) SSLSocketFactory.getDefault());
    SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
                        socket,
                        socket.getInetAddress().getHostAddress(),
                        socket.getPort(),
                        true);
    sslSocket.startHandshake();
    bufferedReader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream(), getEncoding()));
    bufferedWriter = new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream(), getEncoding()));
    socket = sslSocket;
}

Однако с startHandshake() и без него я всегда получаю ошибку recv (без нее я просто получаю ее, когда пытаюсь прочитать из нового буферизованного Reader

java.io.IOException: Can't connect to server
    at org.pircbotx.PircBotX.connect(PircBotX.java:472) [Exception handling]
    at org.pircbotx.PircBotX.connect(PircBotX.java:224) [Overridden constructor]
    at org.pircbotx.impl.PircBotXExample.main(PircBotXExample.java:185)
Caused by: java.net.SocketException: Software caused connection abort: recv failed
    at java.net.SocketInputStream.read(SocketInputStream.java:150)
    at java.net.SocketInputStream.read(SocketInputStream.java:121)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:442)
    at sun.security.ssl.InputRecord.read(InputRecord.java:480)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:927)
    at sun.security.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1707)
    at sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:122)
    at sun.security.ssl.Handshaker.kickstart(Handshaker.java:909)
    at sun.security.ssl.SSLSocketImpl.kickstartHandshake(SSLSocketImpl.java:1423)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1288)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
    at org.pircbotx.PircBotX.connect(PircBotX.java:411) [startHandshake line]
    ... 2 more

Полный журнал (с -Djavax.net.debug=all)

1365707641940 <<<:kenny.chatspike.net 670 PircBotX :STARTTLS successful, go ahead with TLS handshake
keyStore is : 
keyStore type is : jks
keyStore provider is : 
init keystore
init keymanager of type SunX509
trustStore is: C:\Program Files\Java\jdk1.7.0_17\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is : 
init truststore

[*SNIP* A bunch of adding trusted certificates]

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
RandomCookie:  GMT: 1365707642 bytes = { 219, 198, 250, 114, 173, 93, 144, 4, 231, 53, 192, 242, 141, 137, 219, 115, 48, 203, 218, 188, 51, 30, 184, 245, 182, 150, 132, 254 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
***
[write] MD5 and SHA1 hashes:  len = 149
0000: 01 00 00 91 03 01 51 67   0B 7A DB C6 FA 72 AD 5D  ......Qg.z...r.]
0010: 90 04 E7 35 C0 F2 8D 89   DB 73 30 CB DA BC 33 1E  ...5.....s0...3.
0020: B8 F5 B6 96 84 FE 00 00   2A C0 09 C0 13 00 2F C0  ........*...../.
0030: 04 C0 0E 00 33 00 32 C0   07 C0 11 00 05 C0 02 C0  ....3.2.........
0040: 0C C0 08 C0 12 00 0A C0   03 C0 0D 00 16 00 13 00  ................
0050: 04 00 FF 01 00 00 3E 00   0A 00 34 00 32 00 17 00  ......>...4.2...
0060: 01 00 03 00 13 00 15 00   06 00 07 00 09 00 0A 00  ................
0070: 18 00 0B 00 0C 00 19 00   0D 00 0E 00 0F 00 10 00  ................
0080: 11 00 02 00 12 00 04 00   05 00 14 00 08 00 16 00  ................
0090: 0B 00 02 01 00                                     .....
main, WRITE: TLSv1 Handshake, length = 149
main, waiting for close_notify or alert: state 1
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
main, SEND TLSv1 ALERT:  fatal, description = unexpected_message
    at java.net.SocketInputStream.socketRead0(Native Method)
main, WRITE: TLSv1 Alert, length = 2
main, Exception sending alert: java.net.SocketException: Software caused connection abort: socket write error
main, called closeSocket()
1365707642425 *** Disconnected.

Что я могу сделать неправильно, что может вызвать эту ошибку? Я знаю, что эта функция работает, потому что другие клиенты могут это делать, но я просто не могу понять, как это сделать из Java.

Какие-либо предложения?


person TheLQ    schedule 11.04.2013    source источник


Ответы (2)


Я провел некоторое тестирование и получил эту ошибку при получении данных на socket, когда sslSocket устанавливает связь. Это может произойти, если вы, например, зарегистрируетесь (отправите НИК/ЛОГИН) на socket, а не на sslSocket. Попробуйте сделать первую команду, которую вы делаете на сервер после подключения TLSSTART, и дождитесь кода 670/691, прежде чем отправлять что-либо еще.

Следующее не создает для меня никаких исключений и без проблем рукопожатие:

public static void main(String[] args) throws Exception {
    Socket socket = new Socket("irc.chatspike.net",6667);
    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    String line;
    writer.write("STARTTLS\r\n");
    writer.flush(); 
    while ((line = reader.readLine()) != null) {
        if (line.contains(" 670 ")) {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                        public void checkClientTrusted(
                            java.security.cert.X509Certificate[] certs, String authType) {
                        }
                        public void checkServerTrusted(
                            java.security.cert.X509Certificate[] certs, String authType) {
                        }
                    }
                }, new java.security.SecureRandom());
            SSLSocketFactory sslSocketFactory = ((SSLSocketFactory) sc.getSocketFactory());
            SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
                                socket,
                                socket.getInetAddress().getHostAddress(),
                                socket.getPort(),
                                true);
            sslSocket.startHandshake();
        }
    }
person ddmps    schedule 11.04.2013
comment
Отправка STARTTLS в качестве самой первой строки по-прежнему дает мне ошибку recv. Интересно, что создание нового потока для чтения из буферизованного чтения с открытым текстом, чтобы увидеть, не проходит ли что-нибудь, вызвало EOFException: одноранговый узел SSL закрыт неправильно. - person TheLQ; 12.04.2013
comment
После большого количества тестов выяснилось, что проблема заключалась в отправке чего-либо на сервер между STARTTLS и получением строки 670, что, я думаю, заставило сервер разорвать соединение. Спасибо за код, он доказал, что это возможно в Java, поэтому я не сдался. - person TheLQ; 13.04.2013
comment
Проблема заключается не в том, что вы создаете SSLSocket как слой поверх функционирующего сокета. Это именно то, что должен делать STARTTLS. -1 - person user207421; 19.11.2013
comment
@EJP Этот ответ, хотя и технически неверный, доказал, что то, что я делал, возможно, и дал мне направление. Других ответов не последовало, поэтому я принял и добавил +1 к этому. - person TheLQ; 19.11.2013
comment
Было бы неправильно оставить неверное первое предложение без комментариев, чтобы ввести в заблуждение будущих читателей. Что вы делаете со своими голосами и аптиками, это ваша забота. - person user207421; 19.11.2013
comment
@EJP Я имел в виду что-то другое с неясным, теперь удаленным предложением, хотя я думаю, что остальное сделало то, что я имел в виду, очевидным. - person ddmps; 19.11.2013

Проблема здесь в том, что после команды STARTTLS вы отправляете что-то отличное от SSL-рукопожатия через открытый текстовый сокет. После того как вы выпустили STARTTLS и он был принят, использование сокета открытого текста должно быть прекращено.

person user207421    schedule 19.11.2013