TakahikoKawasaki / nv-websocket-client

High-quality WebSocket client implementation in Java.
Apache License 2.0
2.03k stars 292 forks source link

Handshake failed (Android 5 and 6) #187

Open myroniak opened 5 years ago

myroniak commented 5 years ago

I have a code with connection to wss server and I caught the error with Handshake:

SSLContext context = NaiveSSLContext.getInstance("TLS"); new WebSocketFactory() .setSSLContext(context) .setVerifyHostname(false) .setConnectionTimeout(5000) .createSocket(BuildConfig.SOCKETURL) .addListener(webSocketListener) .addExtension(WebSocketExtension.PERMESSAGE_DEFLATE) .connect();

07-03 21:53:00.486 5258-5258/com.blockchain.edcwallet W/System.err: com.neovisionaries.ws.client.WebSocketException: Failed to get the input stream of the raw socket: Handshake failed 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.neovisionaries.ws.client.WebSocket.openInputStream(WebSocket.java:3321) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.neovisionaries.ws.client.WebSocket.shakeHands(WebSocket.java:3279) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.neovisionaries.ws.client.WebSocket.connect(WebSocket.java:2326) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.blockchain.edcwallet.data.network.service.socket.EventServiceImpl.connectWS(EventServiceImpl.java:91) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.blockchain.edcwallet.data.network.service.socket.EventServiceImpl.connect(EventServiceImpl.java:37) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.blockchain.edcwallet.util.AppLifeCycleObserver.onEnterForeground(AppLifeCycleObserver.java:33) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at java.lang.reflect.Method.invoke(Native Method) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at java.lang.reflect.Method.invoke(Method.java:372) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:215) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:193) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:184) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:36) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:355) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:293) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:333) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:138) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:124) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ProcessLifecycleOwner.activityStarted(ProcessLifecycleOwner.java:108) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ProcessLifecycleOwner$2.onStart(ProcessLifecycleOwner.java:80) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ReportFragment.dispatchStart(ReportFragment.java:61) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at androidx.lifecycle.ReportFragment.onStart(ReportFragment.java:81) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.Fragment.performStart(Fragment.java:2077) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:922) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1049) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:1874) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.Activity.performStart(Activity.java:5959) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2261) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.ActivityThread.access$800(ActivityThread.java:144) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.os.Looper.loop(Looper.java:135) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5221) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at java.lang.reflect.Method.invoke(Native Method) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at java.lang.reflect.Method.invoke(Method.java:372) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: Caused by: javax.net.ssl.SSLHandshakeException: Handshake failed 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:598) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:560) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.neovisionaries.ws.client.WebSocket.openInputStream(WebSocket.java:3314) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: ... 37 more 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7f62ea28ab80: Failure in SSL library, usually a protocol error 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:765 0x7f62e1998e90:0x00000000) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:302) 07-03 21:53:00.487 5258-5258/com.blockchain.edcwallet W/System.err: ... 40 more

leoPaim29 commented 5 years ago

I have the same problem. No solution found yet.

leoPaim29 commented 5 years ago

As far as I notice, the connection is done TLSv1.2 but the Handshake is done e TLSv1.0. My server doesn't allow TLSv1.0, what causes the failure.

terje-rosenlund commented 4 years ago

Device: Manufacturer: samsung, Model: GT-S6310N, API: 16 Problem: SSLSocketFactory.getDefault() returns SSLv3 and TLSv1 as enabled protocols on API 16 Default protocols fails on servers with recommended SSL-configurations which disables SSLv3 and TLSv1 (eg. letsencrypt) Using WebSocket.clearProtocols(), WebSocket.addProtocol("TLSv1.1") and WebSocket.addProtocol("TLSv1.2") have no effect

My solution implies that wsClient.getSocket() MUST succeed which is not the case in version 2.9 The reccomended solution is to use WebSocket.getConnectedSocket() instead but it fails so I'm stuck with version 2.8

I ended up using this:

`final String server = "myserver.com";
final String webSocketUri = "wss://" + server;
final WebSocketFactory factory = new WebSocketFactory();
WebSocket wsClient = null;
Socket socket = null;
try {
    factory.setServerName(server);
    wsClient = factory.createSocket(webSocketUri, 5000);
    socket = wsClient.getSocket();
} catch (IOException e) {
    e.printStackTrace();
    return;
}
ArrayList<String> setProtocols = new ArrayList<String>();
for (String supportedProtocol : ((SSLSocket) socket).getSupportedProtocols()) {
    if (supportedProtocol.startsWith("TLSv1.")) {   // Include all TLSv1.[x], skip all others (eg. SSLv[x] and TLSv1)
        setProtocols.add(supportedProtocol);
    }
}
if (setProtocols.size() > 0) {
    ((SSLSocket) socket).setEnabledProtocols(setProtocols.toArray(new String[0]));
}`

Using version 2.9, WebSocket.getConnectedSocket()

NetworkOnMainThreadException:

EDIT 07.12.2021, 11:45

Solved by moving my connect function to a LocalThread but new approach using getConnectedSocket gives new problems, ref. my next comment below

`E/AndroidRuntime: FATAL EXCEPTION: main java.lang.RuntimeException: Unable to start service xxx.Monitor@41e65fe0 with Intent { flg=0x24000000 cmp=xxx.no/xxx.Monitor }: android.os.NetworkOnMainThreadException ...

aamernabi commented 4 years ago

Same issue. Is there any solution?

vlasky commented 3 years ago

@aamernabi there is now! See PR https://github.com/TakahikoKawasaki/nv-websocket-client/pull/225

terje-rosenlund commented 3 years ago

vlasky's modification do not fix the handshake problem for API < 21 The problem is the default protocols in the context returned by getInstance("TLS"):

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
final SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters(); // protocols: {"SSLv3", "TLSv1"}
final String[] supportedProtocols = sslContext.getSupportedSSLParameters().getProtocols(); // {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}

All servers with A-graded security has SSLv3, TLSv1 and TSLv1.1 disabled (ref. https://ssl-config.mozilla.org/) and rejects the request

getInstance on eg. API 28 returns all supported protocols in defaultSSLParameters.protocols: {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}

Default protocols can not be changed so minSdkVersion must be bumped up to 21 unless we get an alternative getInstance("TLS") which returns all supported protocols as default protocols

_Ps! Testing with old config as proposed by mozilla on Ubuntu 20.04.2 LTS is not sufficient to enable TLSv1 and TLSv1.1, you must also add @SECLEVEL=1 to sslchipers (or follow instructions on https://askubuntu.com)

vlasky commented 3 years ago

@terje-rosenlund my modification definitely fixed the handshake problem our clients were experiencing on phones running older Android versions, which were caused by SNI not being automatically enabled.

terje-rosenlund commented 3 years ago

@vlasky I pressume that your modification fixed the handshake problem related to SNI but the problem in this case is caused by unsupported protocol:

SSL_do_handshake() failed (SSL: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol) while SSL handshaking, client: 10.20.30.1, server: 0.0.0.0:443

terje-rosenlund commented 3 years ago

Test with default SSL configuration shows that my code is able to set correct protocols before ws.connect is called (ie. before handshake) but calling ws.connect() do not trigger a call to registered listener for onSendingHandshake

It should have been called by ws.connect → ws.shakeHands → ws.writeHandshake → ws.callOnSendingHandshake before the handshake is sent with output.write(handshake)

Server log shows that the handshake is sent prematurely (with default protocols) when ws.shakeHands calls openInputStream

openInputStream: return new WebSocketInputStream(new BufferedInputStream(socket.getInputStream()));

Debug shows that the premature handshake is triggered by the call to socket.getInputStream() (java.net.Socket) That implies that the handshake has already failed before ws.writeHandshake is called

startHandshake:250, OpenSSLSocketImpl (com.android.org.conscrypt) \:661, OpenSSLSocketImpl$SSLInputStream (com.android.org.conscrypt) getInputStream:632, OpenSSLSocketImpl (com.android.org.conscrypt) ← openInputStream:3342, WebSocket (com.neovisionaries.ws.client) shakeHands:3307, WebSocket (com.neovisionaries.ws.client) connect:2354, WebSocket (com.neovisionaries.ws.client) \:1286, Monitor$1LocalThread (defman) connect:1312, Monitor (defman) onStartCommand:836, Monitor (defman)

I assume that this is a bug?

2 possible duplicate issues: https://github.com/TakahikoKawasaki/nv-websocket-client/issues/154 (May 21, 2018) https://github.com/TakahikoKawasaki/nv-websocket-client/issues/162 (Jun 29, 2018)

Edit: The socket in shakeHands(socket) is an OpenSSLSocketImpl class instance: https://android.googlesource.com/platform/external/conscrypt/+/f087968/src/main/java/org/conscrypt/OpenSSLSocketImpl.java?autodive=0%2F%2F%2F%2F

shakeHands calls openInputStream and openOutputStream before calling writeHandshake

Both methods has implicit calls to startHandshake:

socket.getInputStream → socket.waitForHandshake → socket.startHandshake socket.getOutputStream → socket.waitForHandshake → socket.startHandshake

That means that ws.openInputStream(socket) and ws.openOutputStream(socket) can't be called before custom handshake has been sent and custom handshake is sent with output.write(handshake) which requires a call to ws.openOutputStream(socket) (mutually excluding requirements)