I'm using Kuzzle with his Android SDK (3.0.10) to receive notifications with the pub/sub system.
In Android 5 and 6, when I try to connect to the server an error callback is executed with the message: {"message":"SSL handshake aborted: ssl=0x7f382c540280: I/O error during system call, Connection reset by peer"}
In other versions of Android it works perfectly.
Options options = new Options();
options.setPort(443);
options.setOfflineMode(Mode.AUTO);
options.setSsl(true);
kuzzle = new Kuzzle(kuzzleHost, options, new ResponseListener<Void>() {
@Override
public void onSuccess(Void response) {
initSubscriptions();
}
@Override
public void onError(JSONObject error) {
Log.e("Kuzzle", "error");
}
});
} catch (Exception e) {
Utils.caughtException(KuzzleSub.class.getSimpleName(), e);
}
app/build.gradle dependencies
dependencies {
implementation 'io.kuzzle:sdk-android:3.0.10'
implementation 'tech.gusavila92:java-android-websocket-client:1.2.2'
Debbuging Kuzzle and gusavila92.websocketclient.WebSocketClient classes I have found that in Android 5, the websocket enabled protocols are SSLv3, TLSv1, TLSv1.1, TLSv1.2. However, in Android 7 and greater versions SSLv3 is not enabled.
Searching on the Internet, I have found multiple solutions, like disable SSLv3, enable TLSv1.2 only, create a new SSLFactory. The code below is my attempt to use another SSLFactory creating a class that extends Kuzzle.java and SSLFactory.java
public class ExtendedKuzzle extends Kuzzle {
public ExtendedKuzzle(@NonNull String host, Options options, ResponseListener<Void> connectionCallback) throws URISyntaxException {
super(host, options, connectionCallback);
}
public ExtendedKuzzle(@NonNull String host) throws URISyntaxException {
super(host);
}
public ExtendedKuzzle(@NonNull String host, ResponseListener<Void> cb) throws URISyntaxException {
super(host, cb);
}
public ExtendedKuzzle(@NonNull String host, Options options) throws URISyntaxException {
super(host, options);
}
@Override
protected WebSocketClient createSocket() throws URISyntaxException {
WebSocketClient webSocketClient = super.createSocket();
try {
// SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
// sslContext.init(null, null, null);
webSocketClient.setSSLSocketFactory(new TLSSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
return webSocketClient;
}
}
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
// List<String> enabledProtocols = new ArrayList<>(Arrays.asList(((SSLSocket)socket).getEnabledProtocols()));
// if (enabledProtocols.size() > 1) {
// enabledProtocols.remove("SSLv3");
// ((SSLSocket)socket).setEnabledProtocols(enabledProtocols.toArray(new String[0]));
// }
}
return socket;
}
}
I have also tried to set an "unquestioning" TrustManager without success either:
public static SSLSocketFactory createSslSocketFactory() throws Exception {
TrustManager[] byPassTrustManagers = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
} };
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, byPassTrustManagers, new java.security.SecureRandom());
return sslContext.getSocketFactory();
}
If anyone knows what is happening, I would appreciate it very much. Thanks a lot!