Failure in ssl library usually a protocol error

I am trying to run the following code in android URLConnection l_connection = null; // Create connection uzip=new UnZipData(mContext); l_url = new URL(serverurl); if ("https".equals(l_url.

I found the solution for it by analyzing the data packets using wireshark. What I found is that while making a secure connection, android was falling back to SSLv3 from TLSv1 . It is a bug in android versions < 4.4 , and it can be solved by removing the SSLv3 protocol from Enabled Protocols list. I made a custom socketFactory class called NoSSLv3SocketFactory.java. Use this to make a socketfactory.

/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class NoSSLv3SocketFactory extends SSLSocketFactory{
    private final SSLSocketFactory delegate;

public NoSSLv3SocketFactory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

private Socket makeSocketSafe(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new NoSSLv3SSLSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}

private class NoSSLv3SSLSocket extends DelegateSSLSocket {

    private NoSSLv3SSLSocket(SSLSocket delegate) {
        super(delegate);

    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {

            List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
            if (enabledProtocols.size() > 1) {
                enabledProtocols.remove("SSLv3");
                System.out.println("Removed SSLv3 from enabled protocols");
            } else {
                System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
            }
            protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
        }

        super.setEnabledProtocols(protocols);
    }
}

public class DelegateSSLSocket extends SSLSocket {

    protected final SSLSocket delegate;

    DelegateSSLSocket(SSLSocket delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        return delegate.getEnabledCipherSuites();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        delegate.setEnabledCipherSuites(suites);
    }

    @Override
    public String[] getSupportedProtocols() {
        return delegate.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return delegate.getEnabledProtocols();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        delegate.setEnabledProtocols(protocols);
    }

    @Override
    public SSLSession getSession() {
        return delegate.getSession();
    }

    @Override
    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.addHandshakeCompletedListener(listener);
    }

    @Override
    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.removeHandshakeCompletedListener(listener);
    }

    @Override
    public void startHandshake() throws IOException {
        delegate.startHandshake();
    }

    @Override
    public void setUseClientMode(boolean mode) {
        delegate.setUseClientMode(mode);
    }

    @Override
    public boolean getUseClientMode() {
        return delegate.getUseClientMode();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        delegate.setNeedClientAuth(need);
    }

    @Override
    public void setWantClientAuth(boolean want) {
        delegate.setWantClientAuth(want);
    }

    @Override
    public boolean getNeedClientAuth() {
        return delegate.getNeedClientAuth();
    }

    @Override
    public boolean getWantClientAuth() {
        return delegate.getWantClientAuth();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        delegate.setEnableSessionCreation(flag);
    }

    @Override
    public boolean getEnableSessionCreation() {
        return delegate.getEnableSessionCreation();
    }

    @Override
    public void bind(SocketAddress localAddr) throws IOException {
        delegate.bind(localAddr);
    }

    @Override
    public synchronized void close() throws IOException {
        delegate.close();
    }

    @Override
    public void connect(SocketAddress remoteAddr) throws IOException {
        delegate.connect(remoteAddr);
    }

    @Override
    public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
        delegate.connect(remoteAddr, timeout);
    }

    @Override
    public SocketChannel getChannel() {
        return delegate.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return delegate.getInetAddress();
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return delegate.getInputStream();
    }

    @Override
    public boolean getKeepAlive() throws SocketException {
        return delegate.getKeepAlive();
    }

    @Override
    public InetAddress getLocalAddress() {
        return delegate.getLocalAddress();
    }

    @Override
    public int getLocalPort() {
        return delegate.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return delegate.getLocalSocketAddress();
    }

    @Override
    public boolean getOOBInline() throws SocketException {
        return delegate.getOOBInline();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        return delegate.getOutputStream();
    }

    @Override
    public int getPort() {
        return delegate.getPort();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return delegate.getReceiveBufferSize();
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return delegate.getRemoteSocketAddress();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return delegate.getReuseAddress();
    }

    @Override
    public synchronized int getSendBufferSize() throws SocketException {
        return delegate.getSendBufferSize();
    }

    @Override
    public int getSoLinger() throws SocketException {
        return delegate.getSoLinger();
    }

    @Override
    public synchronized int getSoTimeout() throws SocketException {
        return delegate.getSoTimeout();
    }

    @Override
    public boolean getTcpNoDelay() throws SocketException {
        return delegate.getTcpNoDelay();
    }

    @Override
    public int getTrafficClass() throws SocketException {
        return delegate.getTrafficClass();
    }

    @Override
    public boolean isBound() {
        return delegate.isBound();
    }

    @Override
    public boolean isClosed() {
        return delegate.isClosed();
    }

    @Override
    public boolean isConnected() {
        return delegate.isConnected();
    }

    @Override
    public boolean isInputShutdown() {
        return delegate.isInputShutdown();
    }

    @Override
    public boolean isOutputShutdown() {
        return delegate.isOutputShutdown();
    }

    @Override
    public void sendUrgentData(int value) throws IOException {
        delegate.sendUrgentData(value);
    }

    @Override
    public void setKeepAlive(boolean keepAlive) throws SocketException {
        delegate.setKeepAlive(keepAlive);
    }

    @Override
    public void setOOBInline(boolean oobinline) throws SocketException {
        delegate.setOOBInline(oobinline);
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        delegate.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean reuse) throws SocketException {
        delegate.setReuseAddress(reuse);
    }

    @Override
    public synchronized void setSendBufferSize(int size) throws SocketException {
        delegate.setSendBufferSize(size);
    }

    @Override
    public void setSoLinger(boolean on, int timeout) throws SocketException {
        delegate.setSoLinger(on, timeout);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        delegate.setSoTimeout(timeout);
    }

    @Override
    public void setTcpNoDelay(boolean on) throws SocketException {
        delegate.setTcpNoDelay(on);
    }

    @Override
    public void setTrafficClass(int value) throws SocketException {
        delegate.setTrafficClass(value);
    }

    @Override
    public void shutdownInput() throws IOException {
        delegate.shutdownInput();
    }

    @Override
    public void shutdownOutput() throws IOException {
        delegate.shutdownOutput();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object o) {
        return delegate.equals(o);
    }
}
}

Use this class like this while connecting :

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();

UPDATE :

Now, correct solution would be to install a newer security provider using Google Play Services:

    ProviderInstaller.installIfNeeded(getApplicationContext());

This effectively gives your app access to a newer version of OpenSSL and Java Security Provider, which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

Or you can restrict the enabled protocols using engine.setEnabledProtocols.

Don’t forget to add the following dependency (check the latest version here):

implementation 'com.google.android.gms:play-services-auth:17.0.0'

For more info, checkout this link.

I’m trying to download some content using the URL class with a given link that comes from the server.

My code to download is it:

            URL url = new URL(downloadUrl);
            InputStream stream = url.openStream();
            byte[] content = new byte[stream.available()];
            stream.read(content);
            stream.close();

But when running I got the following exception:

 java.io.IOException: SSL handshake failure: Failure in SSL library, usually a protocol error
 error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:604 0xaf076228:0x00000000)
     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.nativeconnect(Native Method)
 ...

The link I was using is something like:

https://contentserver.com/d/761/34/215656/5de1a41ea3bc9c81978af95ed19b03286f64d9a3

I know if I enter it on browser it donwload an File, I want download the same file throught Java.

Thanks

asked Mar 9, 2011 at 18:36

Marcos Vasconcelos's user avatar

2

Code to read data from the https url in java

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.*;
import java.security.Security;
import java.util.Properties;

public class UseHttps {

public static void main(String argv[]) {

String fullURL = "https://fortress.wa.gov/lni/bbip/detail.aspx?License=SIBLUCL004C5";
try {

    URL page = new URL(fullURL); // Process the URL far enough to find the right handler
    URLConnection urlc = page.openConnection();
    urlc.setUseCaches(false); // Don't look at possibly cached data
    System.out.println("Content-type = " + urlc.getContentType()); // See what's here
    System.out.println("Content-length = " + urlc.getContentLength()); // See how much of it there is
    // Read it all and print it out
    BufferedReader br = new BufferedReader(new InputStreamReader(urlc.getInputStream()));
    String buffer = "";
    while (buffer != null) {
        try {
            System.out.println(buffer);
            buffer = br.readLine();
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
            break;
        }
    }
}
catch (MalformedURLException mue) {
System.out.println(fullURL + " is not a URL that can be resolved");
}
catch (IOException ie) {
ie.printStackTrace();
}
}
}

answered May 19, 2011 at 5:50

Shaunak's user avatar

Marcos,

this might be completely irrelevant, but… I was getting the same error…

abort: error: _ssl.c:490: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

… trying to do an «hg clone» on my bitbucket.org mercurial repo…, I was trying to connect to bitbucket over https from the windows command line… it turned out that I was supplying the incorrect password to my proxy…

answered Mar 21, 2011 at 4:00

mlo55's user avatar

mlo55mlo55

6,5236 gold badges33 silver badges26 bronze badges

While I am not sure about your SSL error, the way you are reading data is most likely NOT what you want. InputStream.available() is not the amount of data in the stream. The stream is by definition «unbounded», and it is over only when it is over. InputStream does not know how many bytes it has. Method available() simply tells you how many bytes can be read without blocking on IO.

answered May 18, 2011 at 18:20

igorlord's user avatar

igorlordigorlord

3663 silver badges4 bronze badges

public class MainActivity extends Activity

    implements ProviderInstaller.ProviderInstallListener {

  private static final int ERROR_DIALOG_REQUEST_CODE = 1;

  private boolean retryProviderInstall;

  //Update the security provider when the activity is created.

  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    if (Build.VERSION.SDK_INT in 17..20) {

        ProviderInstaller.installIfNeededAsync(this, this);

      }

  }

  /**

   * This method is only called if the provider is successfully updated

   * (or is already up-to-date).

   */

  @Override

  protected void onProviderInstalled() {

    // Provider is up-to-date, app can make secure network calls.

  }

  /**

   * This method is called if updating fails; the error code indicates

   * whether the error is recoverable.

   */

  @Override

  protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {

    GoogleApiAvailability availability = GoogleApiAvailability.getInstance();

    if (availability.isUserRecoverableError(errorCode)) {

      // Recoverable error. Show a dialog prompting the user to

      // install/update/enable Google Play services.

      availability.showErrorDialogFragment(

          this,

          errorCode,

          ERROR_DIALOG_REQUEST_CODE,

          new DialogInterface.OnCancelListener() {

            @Override

            public void onCancel(DialogInterface dialog) {

              // The user chose not to take the recovery action

              onProviderInstallerNotAvailable();

            }

          });

    } else {

      // Google Play services is not available.

      onProviderInstallerNotAvailable();

    }

  }

  @Override

  protected void onActivityResult(int requestCode, int resultCode,

      Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ERROR_DIALOG_REQUEST_CODE) {

      // Adding a fragment via GoogleApiAvailability.showErrorDialogFragment

      // before the instance state is restored throws an error. So instead,

      // set a flag here, which will cause the fragment to delay until

      // onPostResume.

      retryProviderInstall = true;

    }

  }

  /**

   * On resume, check to see if we flagged that we need to reinstall the

   * provider.

   */

  @Override

  protected void onPostResume() {

    super.onPostResume();

    if (retryProviderInstall) {

      // We can now safely retry installation.

      ProviderInstaller.installIfNeededAsync(this, this);

    }

    retryProviderInstall = false;

  }

  private void onProviderInstallerNotAvailable() {

    // This is reached if the provider cannot be updated for some reason.

    // App should consider all HTTP communication to be vulnerable, and take

    // appropriate action.

  }

}

I’m getting the following error while trying to run on devices below android lollipop and it is working really well for version above lollipop.

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x943e670: Failure in SSL library, usually a protocol error
    error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb750d3a1:0x00000000)

this is my method to register the user:

        String tag_string_req = "register";
            StringRequest strReq = new StringRequest(Request.Method.POST,
                    AppConfig.URL_REGISTER, new Response.Listener<String>() {

                @Override
                public void onResponse(String response) {
                    Log.d(TAG, "Register Response: " + response.toString());
                    hideDialog();

                    try {
                        JSONObject jObj = new JSONObject(response);
                        boolean error = jObj.getBoolean("error");
                        if (!error) {

                            String id = jObj.getString("id");
                            finish();
                        } else {
                            String errorMsg = jObj.getString("error_msg");
                            Toast.makeText(getApplicationContext(),
                                    errorMsg, Toast.LENGTH_LONG).show();
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }, new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e(TAG, "Registration Error: " + error.getMessage());
                    Toast.makeText(getApplicationContext(),
                            error.getMessage(), Toast.LENGTH_LONG).show();
                    hideDialog();
                }
            }) {

                @Override
                protected Map<String, String> getParams() {
                    Map<String, String> params = new HashMap<String, String>();
                    params.put("user_name", txtSEditName.getText().toString().trim());
                    params.put("password", txtSEditPhone.getText().toString().trim());

                    return params;
                }

            };
            AppController.getInstance().addToRequestQueue(strReq, tag_string_req);

My volley singleton looks like this:

public class AppController extends Application {

    public static final String TAG = AppController.class.getSimpleName();

    private RequestQueue mRequestQueue;

    private static AppController mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized AppController getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

and my app config file looks like this:

public static String URL_REGISTER = "http://192.168.56.1/MyApp/register.php";

Can anyone help me solve this issue?

Error: javax.net.ssl.SSLHandshakeException:
javax.net.ssl.SSLProtocolException: SSL handshake aborted:

Advertisement

Answer

I have tried to sort out the error and this has worked for me. What I have understood was that for devices below that of lollipop the protocols TLSv1.1 and TLSv1.2 are not enabled by default. Inorder to enable them for devices such using jellybean or kitkat we will have to use a SSLSocketFactory.

So now I have made the following change to the getRequestQueue() method of Volley singleton:

public RequestQueue getRequestQueue() {


    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
            && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        Log.d("msg", "HI, I m a kitkat phone");
        try {
            ProviderInstaller.installIfNeeded(getApplicationContext());
        } catch (GooglePlayServicesRepairableException e) {
            // Indicates that Google Play services is out of date, disabled, etc.
            // Prompt the user to install/update/enable Google Play services.
            GooglePlayServicesUtil.showErrorNotification(e.getConnectionStatusCode(), getApplicationContext());
            e.printStackTrace();

        } catch (GooglePlayServicesNotAvailableException e) {
            // Indicates a non-recoverable error; the ProviderInstaller is not able
            // to install an up-to-date Provider.

            e.printStackTrace();
        }

        HttpStack stack = null;
        try {
            stack = new HurlStack(null, new TLSSocketFactory());
        } catch (KeyManagementException e) {
            e.printStackTrace();
            Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
            stack = new HurlStack();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
            stack = new HurlStack();
        }
        mRequestQueue = Volley.newRequestQueue(getApplicationContext(), stack);
    } else {
        mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    }
    return mRequestQueue;
}

And create a class named TLSSocketFactory.java and add the following code:

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.1", "TLSv1.2"});
    }
    return socket;
}

}

One more step is to add the following implementation in your dependencies of gradle file:

implementation 'com.google.android.gms:play-services-base:11.0.0'

I hope this might help you to resolve this issue.

4 People found this is helpful

I found the solution for it by analyzing the data packets using wireshark. What I found is that while making a secure connection, android was falling back to SSLv3 from TLSv1 . It is a bug in android versions < 4.4 , and it can be solved by removing the SSLv3 protocol from Enabled Protocols list. I made a custom socketFactory class called NoSSLv3SocketFactory.java. Use this to make a socketfactory.

/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class NoSSLv3SocketFactory extends SSLSocketFactory{
    private final SSLSocketFactory delegate;

public NoSSLv3SocketFactory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

private Socket makeSocketSafe(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new NoSSLv3SSLSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}

private class NoSSLv3SSLSocket extends DelegateSSLSocket {

    private NoSSLv3SSLSocket(SSLSocket delegate) {
        super(delegate);

    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {

            List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
            if (enabledProtocols.size() > 1) {
                enabledProtocols.remove("SSLv3");
                System.out.println("Removed SSLv3 from enabled protocols");
            } else {
                System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
            }
            protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
        }

        super.setEnabledProtocols(protocols);
    }
}

public class DelegateSSLSocket extends SSLSocket {

    protected final SSLSocket delegate;

    DelegateSSLSocket(SSLSocket delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        return delegate.getEnabledCipherSuites();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        delegate.setEnabledCipherSuites(suites);
    }

    @Override
    public String[] getSupportedProtocols() {
        return delegate.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return delegate.getEnabledProtocols();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        delegate.setEnabledProtocols(protocols);
    }

    @Override
    public SSLSession getSession() {
        return delegate.getSession();
    }

    @Override
    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.addHandshakeCompletedListener(listener);
    }

    @Override
    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.removeHandshakeCompletedListener(listener);
    }

    @Override
    public void startHandshake() throws IOException {
        delegate.startHandshake();
    }

    @Override
    public void setUseClientMode(boolean mode) {
        delegate.setUseClientMode(mode);
    }

    @Override
    public boolean getUseClientMode() {
        return delegate.getUseClientMode();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        delegate.setNeedClientAuth(need);
    }

    @Override
    public void setWantClientAuth(boolean want) {
        delegate.setWantClientAuth(want);
    }

    @Override
    public boolean getNeedClientAuth() {
        return delegate.getNeedClientAuth();
    }

    @Override
    public boolean getWantClientAuth() {
        return delegate.getWantClientAuth();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        delegate.setEnableSessionCreation(flag);
    }

    @Override
    public boolean getEnableSessionCreation() {
        return delegate.getEnableSessionCreation();
    }

    @Override
    public void bind(SocketAddress localAddr) throws IOException {
        delegate.bind(localAddr);
    }

    @Override
    public synchronized void close() throws IOException {
        delegate.close();
    }

    @Override
    public void connect(SocketAddress remoteAddr) throws IOException {
        delegate.connect(remoteAddr);
    }

    @Override
    public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
        delegate.connect(remoteAddr, timeout);
    }

    @Override
    public SocketChannel getChannel() {
        return delegate.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return delegate.getInetAddress();
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return delegate.getInputStream();
    }

    @Override
    public boolean getKeepAlive() throws SocketException {
        return delegate.getKeepAlive();
    }

    @Override
    public InetAddress getLocalAddress() {
        return delegate.getLocalAddress();
    }

    @Override
    public int getLocalPort() {
        return delegate.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return delegate.getLocalSocketAddress();
    }

    @Override
    public boolean getOOBInline() throws SocketException {
        return delegate.getOOBInline();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        return delegate.getOutputStream();
    }

    @Override
    public int getPort() {
        return delegate.getPort();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return delegate.getReceiveBufferSize();
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return delegate.getRemoteSocketAddress();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return delegate.getReuseAddress();
    }

    @Override
    public synchronized int getSendBufferSize() throws SocketException {
        return delegate.getSendBufferSize();
    }

    @Override
    public int getSoLinger() throws SocketException {
        return delegate.getSoLinger();
    }

    @Override
    public synchronized int getSoTimeout() throws SocketException {
        return delegate.getSoTimeout();
    }

    @Override
    public boolean getTcpNoDelay() throws SocketException {
        return delegate.getTcpNoDelay();
    }

    @Override
    public int getTrafficClass() throws SocketException {
        return delegate.getTrafficClass();
    }

    @Override
    public boolean isBound() {
        return delegate.isBound();
    }

    @Override
    public boolean isClosed() {
        return delegate.isClosed();
    }

    @Override
    public boolean isConnected() {
        return delegate.isConnected();
    }

    @Override
    public boolean isInputShutdown() {
        return delegate.isInputShutdown();
    }

    @Override
    public boolean isOutputShutdown() {
        return delegate.isOutputShutdown();
    }

    @Override
    public void sendUrgentData(int value) throws IOException {
        delegate.sendUrgentData(value);
    }

    @Override
    public void setKeepAlive(boolean keepAlive) throws SocketException {
        delegate.setKeepAlive(keepAlive);
    }

    @Override
    public void setOOBInline(boolean oobinline) throws SocketException {
        delegate.setOOBInline(oobinline);
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        delegate.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean reuse) throws SocketException {
        delegate.setReuseAddress(reuse);
    }

    @Override
    public synchronized void setSendBufferSize(int size) throws SocketException {
        delegate.setSendBufferSize(size);
    }

    @Override
    public void setSoLinger(boolean on, int timeout) throws SocketException {
        delegate.setSoLinger(on, timeout);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        delegate.setSoTimeout(timeout);
    }

    @Override
    public void setTcpNoDelay(boolean on) throws SocketException {
        delegate.setTcpNoDelay(on);
    }

    @Override
    public void setTrafficClass(int value) throws SocketException {
        delegate.setTrafficClass(value);
    }

    @Override
    public void shutdownInput() throws IOException {
        delegate.shutdownInput();
    }

    @Override
    public void shutdownOutput() throws IOException {
        delegate.shutdownOutput();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object o) {
        return delegate.equals(o);
    }
}
}

Use this class like this while connecting :

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();

UPDATE :

Now, correct solution would be to install a newer security provider using Google Play Services:

    ProviderInstaller.installIfNeeded(getApplicationContext());

This effectively gives your app access to a newer version of OpenSSL and Java Security Provider, which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

Or you can restrict the enabled protocols using engine.setEnabledProtocols.

Don’t forget to add the following dependency (check the latest version here):

implementation 'com.google.android.gms:play-services-auth:17.0.0'

For more info, checkout this link.

I have a very weird issue with my phone (Alcatel 997D running Android 4.0.4). Let me start at the beginning. I provide certain services (mostly syncing stuff, Firefox and Cal/CardDAV) on a server with self signed SSL keys that is in my home network. To provide connectivity, I poked a small hole in my router’s firewall and have a domain registered which is set to my WAN IP address regularly.

All worked fine some time ago (6 months, I think). But now my phone can’t connect to my server when it uses the 3G connection (with all the sync apps, Firefox, Cal/CardDAV sync apps, Firefox with my WAN IP as address), but all works fine when I connect through the local WLAN.

Basically, the only things I changed are the SSL keys on my server, and maybe I installed some small Android updates on the phone.
To rule out that it is a problem with the 3G network, I poked another hole in my router’s firewall and sent the traffic to one of my other computers (displaying a small web page over http). To my astonishment, the phone could connect to the page over 3G.

This is very unsettling. To rule out that the phone is the problem, I still need to check 3G connectivity on another machine, and try to connect to my server over a foreign WLAN. But just in case: How can I get more information on the Android side? All apps provide more or less meaningless output.

While googleing, I found out that there are more options available for debugging, like installing curl. I’ll do this, but ask this question anyway in hope that somebody has seen something similar, or can point me to a trivial error on my side.


There are errors in the system log which indicate SSL handshake problems over the 3G connection. Here are some log entries:

[ 08-24 12:30:02.508 19763:0x4d6c E/NativeCrypto ]
Unknown error during handshake
[ 08-24 12:30:08.274 19763:0x4d6c E/NativeCrypto ]
Unknown error during handshake
[ 08-24 12:30:08.275 19763:0x4d6c W/System.err ]
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x18e8508: Failure in SSL library, usually a protocol error
[ 08-24 12:30:08.275 19763:0x4d6c W/System.err ]
error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:683 0x4027f522:0x00000000)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:460)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:257)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:486)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:450)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:194)
[ 08-24 12:30:08.276 19763:0x4d6c W/System.err ]
    at libcore.net.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:280)

[…]

Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x18e8508: Failure in SSL library, usually a protocol error
[ 08-24 12:30:08.277 19763:0x4d6c W/System.err ]
error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:683 0x4027f522:0x00000000)
[ 08-24 12:30:08.278 19763:0x4d6c W/System.err ]
    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
[ 08-24 12:30:08.278 19763:0x4d6c W/System.err ]
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:410)
[ 08-24 12:30:08.278 19763:0x4d6c W/System.err ]

Connection to other SSL pages, e.g. Mozilla Add-Ons, works with 3G. So I guess that there is something about my SSL keys that the telco or Android do not like. I’ll run some tests with other devices and keys.


I tested the 3G network of my provider with a mobile USB modem attached to my Mac, and all just worked, calendar, address book etc (I deactivated Ethernet and WLAN on the Mac, and the application provided by the telco showed 1 MB throughput, and all was a bit sluggish). I think that proves that Android has something to do with this mess.


OK, something that just came to me: I had one app used for calendar syncing that couldn’t sync over WLAN after the SSL certificate switch anymore. It threw an exception that complained about an unsupported or unknown critical extension. I think that is the key to solve the problem.


I changed my server certificate and let Android trust the CA, now I have no trouble with untusted certificates over WLAN, the 3G issue remains however. I dug up this about

SSL23_GET_SERVER_HELLO:unknown protocol

according to this, the error:140770FC I got would point to a DNS issue, which would explain why it happens over 3G only, but not why it doesn’t happen on the Mac.

To gather a bit more information, I tried to open one of my web pages with Chrome. It issued a

Error 111 (net::ERR_TUNNEL_CONNECTION_FAILED)

which was connected to SSL issues here, but I can’t acknowledge that the error disappears after a switch to the standard port.
I need better tools for further investigation, maybe I ask another question later.

Erel


  • #2

Are you sure that you are connecting to the SSL port?

LucaMs


  • #3

Yes, it was an old project I modified just to use SSL, then same port.

Also, if I had set the wrong port…

The server starts and it is reachable (ping)

Erel


  • #4

My guess is that the port is wrong.

LucaMs


  • #5

Unfortunately not.

client b4a:
Public Const ServerURL As String = «wss://xxxxxxxxx:52650/xxxx»

server b4j:
ConfigureSSL(52650)

Erel


  • #6

You should provide more information.

Example of a working ssl connection using the libraries you mentioned:

Dim ws As WebSocket
ws.Initialize("ws")
ws.Connect("wss://b4x.com:51041/smiley/ws")
Wait For ws_Connected
Log("connected")
Wait For ws_TextMessage (Message As String)
Log(Message)

LucaMs


  • #7

You should provide more information.

I don’t know what informations.

My code is very similar to yours.

Just one thing: creating the keyvaluestore I had to use, temporarily, an IP address; but I think that if this were the problem I would not reach the server (I pinged the server from an online service).

The old project works perfectly if I do not use SSL.

I found this:
https://stackoverflow.com/questions…on-javax-net-ssl-sslprotocolexception-ssl-han

where they say that there are problems with Android < 4.4 but I’m using Android 4.4.2

LucaMs


  • #8

Tried this code in my app:

Private Sub TryToConnect
'    wsh.Initialize(Me, "wsh")
'    wsh.ws.Connect(Main.ServerURL)

    NewWS.Initialize("NewWs")
    NewWS.Connect("wss://b4x.com:51041/smiley/ws")
    Wait For NewWs_Connected
    Log("connected here")
    Wait For NewWs_TextMessage (Message As String)
    Log(Message)
    ExitApplication
End Sub

No logs (TryToConnect starts, of course)

This works (browser):
https://www.b4x.com:51041/smiley/index.html

Star-Dust


  • #9

@LucaMs
To make the ranking of your game safe, use FireBase and not the SSL protocol that in some way can be sniffed by Hacker and used to induce false scores.

With Firebase the App that sends will be authenticated.
With SSL you can not identify the genuineness of the sending app. This is the main problem for security.

Better that you talk to an expert about it rather than improvise solutions

Erel


  • #10

Please try the attached project and post the logs.

  • WSS.zip

    7.9 KB · Views: 535

LucaMs


  • #11

Please try the attached project and post the logs.

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Connecting
** Activity (main) Resume **

Erel


  • #12

Add this sub:

Sub ws_Closed (Reason As String)
   Log("error: " & Reason)
End Sub

Is it raised? Are you testing it on a real device?

LucaMs


  • #13

@LucaMs
To make the ranking of your game safe, use FireBase and not the SSL protocol that in some way can be sniffed by Hacker and used to induce false scores.

With Firebase the App that sends will be authenticated.
With SSL you can not identify the genuineness of the sending app. This is the main problem for security.

Better that you talk to an expert about it rather than improvise solutions

I’m trying this for A9, but it’s the same.

Anyway, I will not use only SSL, I think.

Thank you

LucaMs


  • #14

Add this sub:

Sub ws_Closed (Reason As String)
   Log("error: " & Reason)
End Sub

Is it raised? Are you testing it on a real device?

Added.

Real device (Android 4.4.2) and, now, in release mode.

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Connecting
** Activity (main) Resume **
error: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6355af90: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x61803166:0x00000000)

Star-Dust


  • #15

But the @Erel code is very useful for me, thank you

LucaMs


  • #16

I know what the bug is: I AM THE BUG! Every day at least one abnormal problem!
:mad:
:)

This is LUCK

LucaMs


  • #17

It’s the same log (Erel’s code) using a different old device, Android 4.0.4.

Erel


  • #18

I also get this error on an AVD running Android 4. Checking this…

Erel


  • #19

You can try the attached project. It uses a Google service to update the security provider. It didn’t work on the emulator (the ProviderInstaller failed).

The code is in the starter service.

  • wss.zip

    8.4 KB · Views: 655

LucaMs


  • #20

It almost works, since connects, but after 2 seconds:

Понравилась статья? Поделить с друзьями:
  • Failure getting nut ups alias socket error
  • Failure error send method file
  • Failure during update process fifa 23 ошибка
  • Failure delete failed internal error
  • Failure code 0x80004005 ns error failure