TakahikoKawasaki / nv-websocket-client

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

TLS: HostnameUnverifiedException in WebSocket.connectAsynchronously() #126

Open or-else opened 6 years ago

or-else commented 6 years ago

Android SDK version 25 in Android emulator.

I keep getting HostnameUnverifiedException thrown from SocketConnector.verifyHostname(SSLSocket socket, String hostname) at SocketConnector.java line 171 because OkHostnameVerifier.verify(String host, SSLSession session) returns false in SocketConnector.java line 164. That in turn happens because Certificate[] certificates = session.getPeerCertificates(); in OkHostnameVerifier.java line 64 throws javax.net.ssl.SSLPeerUnverifiedException: No peer certificate.

The culprit I believe is the call SSLSession session = socket.getSession(); in SocketConnector.java line 161 Before the call the socket is connected: Socket[address=api.tinode.co/104.196.16.37,port=443,localPort=54910] during the call it gets closed: Socket[unconnected] and the session is returned as com.android.org.conscrypt.SSLNullSession which obviously knows nothing about the host name.

The connection is created with the default socketFactory:

private static WebSocketFactory sWSFactory = new WebSocketFactory();
...
WebSocket ws = sWSFactory.createSocket(mEndpoint, CONNECTION_TIMEOUT); ws.addHeader(...);
ws.addListener(...);
...
ws.connectAsynchronously();

See full source here: https://github.com/tinode/android-example/blob/devel/tinodesdk/src/main/java/co/tinode/tinodesdk/Connection.java

The host is secured with a Letsencrypt certificate which seems to be perfectly fine: https://www.ssllabs.com/ssltest/analyze.html?d=api.tinode.co It works fine in the browser in the same Android emulator. The websocket also works fine without the SSL.

What's going on? Is this a known issue?

Btw, because verifyHostname throws a custom HostnameUnverifiedException instead of re-throwing javax.net.ssl.SSLPeerUnverifiedException, it loses the stack trace and makes it a lot harder to debug the problem. For instance, instead of just posting the stack trace I have to write it up like this. What's the value in replacing SSLPeerUnverifiedException with a custom one?

or-else commented 6 years ago

OK, I figured it out. This is the same bug as https://github.com/TakahikoKawasaki/nv-websocket-client/issues/106

Golang's Letsencrypt implementation requires client to support SNI. Android's socket.getSession() starts handshake implicitly but does not expose exceptions if the handshake fails, just silently closes the socket (https://issuetracker.google.com/issues/37046928). The handshake fails because the server closes the connection because the client sends blank ServerName in ClientHello. And that happens because SNI is not supported as correctly pointed out in the https://github.com/TakahikoKawasaki/nv-websocket-client/issues/106

Any chance to have https://github.com/TakahikoKawasaki/nv-websocket-client/issues/106 fixed any time soon?

or-else commented 6 years ago

Beside the solution in PR #109 , another option is to add another method to WsSocketFactory which would take Socket is one of the parameters, like this:

https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLSocketFactory.html#createSocket(java.net.Socket,%20java.lang.String,%20int,%20boolean)

public abstract Socket createSocket(Socket s,
                  String host,
                  int port,
                  boolean autoClose)
                             throws IOException

This way I could provide my own properly configured socket.

This issue is a show stopper for me. Please talk to me. Let me know if this can be addressed any time soon.

or-else commented 6 years ago

Or add a method WebSocket.setSocket(Socket s) like https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/java/org/java_websocket/client/WebSocketClient.java#L487 does.

dajnz commented 5 years ago

@or-else have you switched to any other library which can work properly with WSS websockets and Let's Encrypt certificates on back-end?

or-else commented 5 years ago

Yes, I've switched to https://github.com/TooTallNate/Java-WebSocket/