Himanshu Likhyani
Himanshu Likhyani

Reputation: 4580

How to enable TLS 1.2 support in an Android application (running on Android 4.1 JB)

As per the docs in Android for SSLSocket and SSLContext, TLS v1.1 and v1.2 protocols are supported in API level 16+, but are not enabled by default. http://developer.android.com/reference/javax/net/ssl/SSLSocket.html http://developer.android.com/reference/javax/net/ssl/SSLContext.html

How do I enable it on a device running Android 4.1 or later (but below 5.0)?

I have tried creating a custom SSLSocketFactory which enables all the supported protocols when Socket's are created and later use my custom implementation as:

HttpsURLConnection.setDefaultSSLSocketFactory(new MySSLSocketFactory());

public class MySSLSocketFactory extends SSLSocketFactory {
        
        private SSLContext sc;
        private SSLSocketFactory ssf;  
        
        public MySSLSocketFactory() {
            try {
                sc = SSLContext.getInstance("TLS");
                sc.init(null, null, null);
                ssf = sc.getSocketFactory();

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }  
        }
        
        @Override
        public Socket createSocket(Socket s, String host, int port, boolean autoClose)
                throws IOException {
            SSLSocket ss = (SSLSocket) ssf.createSocket(s, host, port, autoClose);
            ss.setEnabledProtocols(ss.getSupportedProtocols());
            ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
            return ss;
        }

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

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

        @Override
        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
            SSLSocket ss = (SSLSocket) ssf.createSocket(host, port);
            ss.setEnabledProtocols(ss.getSupportedProtocols());
            ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
            return ss;
        }

        @Override
        public Socket createSocket(InetAddress host, int port) throws IOException {
            SSLSocket ss = (SSLSocket) ssf.createSocket(host, port);
            ss.setEnabledProtocols(ss.getSupportedProtocols());
            ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
            return ss;
        }

        @Override
        public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
                throws IOException, UnknownHostException {
            SSLSocket ss = (SSLSocket) ssf.createSocket(host, port, localHost, localPort);
            ss.setEnabledProtocols(ss.getSupportedProtocols());
            ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
            return ss;
        }

        @Override
        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
                int localPort) throws IOException {
            SSLSocket ss = (SSLSocket) ssf.createSocket(address, port, localAddress, localPort);
            ss.setEnabledProtocols(ss.getSupportedProtocols());
            ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
            return ss;
        }
    }

But it still gives an exception while trying to establish a connection with a server on which Only TLS 1.2 is enabled.

Here is the exception I get:

03-09 09:21:38.427: W/System.err(2496): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb7fa0620: Failure in SSL library, usually a protocol error

03-09 09:21:38.427: W/System.err(2496): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0xa90e6990:0x00000000)

Upvotes: 75

Views: 133885

Answers (9)

Fedor
Fedor

Reputation: 43422

TLS implementation may still have issues on older devices. But you can package a new one with your app this way:

implementation 'org.conscrypt:conscrypt-android:1.0.1'

and

Security.insertProviderAt(Conscrypt.newProvider(), 1);

After that everything works fine for me. But apk size is affected.

Upvotes: 0

francis
francis

Reputation: 4515

This worked for me: Enable TLS1.1 and TLS1.2 in Android

Upvotes: 0

sajad abbasi
sajad abbasi

Reputation: 2184

Add play-services-safetynet library in android build.gradle:

implementation 'com.google.android.gms:play-services-safetynet:+'

and add this code to your MainApplication.java:

@Override
  public void onCreate() {
    super.onCreate();
    upgradeSecurityProvider();
    SoLoader.init(this, /* native exopackage */ false);
  }

  private void upgradeSecurityProvider() {
    ProviderInstaller.installIfNeededAsync(this, new ProviderInstallListener() {
      @Override
      public void onProviderInstalled() {

      }

      @Override
      public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
//        GooglePlayServicesUtil.showErrorNotification(errorCode, MainApplication.this);
        GoogleApiAvailability.getInstance().showErrorNotification(MainApplication.this, errorCode);
      }
    });
  }

Upvotes: 10

onmyway133
onmyway133

Reputation: 48185

As the OP said, TLS v1.1 and v1.2 protocols are supported in API level 16+, but are not enabled by default, we just need to enable it.

Example here uses HttpsUrlConnection, not HttpUrlConnection. Follow https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/, we can create a factory

class MyFactory extends SSLSocketFactory {

    private javax.net.ssl.SSLSocketFactory internalSSLSocketFactory;

    public MyFactory() 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;
    }
}

No matter which Networking library you use, make sure ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); gets called so the Socket has enabled TLS protocols.

Now, you can use that in HttpsUrlConnection

class MyHttpRequestTask extends AsyncTask<String,Integer,String> {

    @Override
    protected String doInBackground(String... params) {
        String my_url = params[0];
        try {
            URL url = new URL(my_url);
            HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection();
            httpURLConnection.setSSLSocketFactory(new MyFactory());
            // setting the  Request Method Type
            httpURLConnection.setRequestMethod("GET");
            // adding the headers for request
            httpURLConnection.setRequestProperty("Content-Type", "application/json");


            String result = readStream(httpURLConnection.getInputStream());
            Log.e("My Networking", "We have data" + result.toString());


        }catch (Exception e){
            e.printStackTrace();
            Log.e("My Networking", "Oh no, error occurred " + e.toString());
        }

        return null;
    }

    private static String readStream(InputStream is) throws IOException {
        final BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("US-ASCII")));
        StringBuilder total = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            total.append(line);
        }
        if (reader != null) {
            reader.close();
        }
        return total.toString();
    }
}

For example

new MyHttpRequestTask().execute(myUrl);

Also, remember to bump minSdkVersion in build.gradle to 16

minSdkVersion 16

Upvotes: 0

carlol
carlol

Reputation: 2260

I solved this issue following the indication provided in the article http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/ with little changes.

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
SSLSocketFactory noSSLv3Factory = null;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
    noSSLv3Factory = new TLSSocketFactory(sslContext.getSocketFactory());
} else {
    noSSLv3Factory = sslContext.getSocketFactory();
}
connection.setSSLSocketFactory(noSSLv3Factory);

This is the code of the custom TLSSocketFactory:

public static class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory internalSSLSocketFactory;

    public TLSSocketFactory(SSLSocketFactory delegate) throws KeyManagementException, NoSuchAlgorithmException {
        internalSSLSocketFactory = delegate;
    }

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

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

    @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));
    }

    /*
     * Utility methods
     */

    private static Socket enableTLSOnSocket(Socket socket) {
        if (socket != null && (socket instanceof SSLSocket)
                && isTLSServerEnabled((SSLSocket) socket)) { // skip the fix if server doesn't provide there TLS version
            ((SSLSocket) socket).setEnabledProtocols(new String[]{TLS_v1_1, TLS_v1_2});
        }
        return socket;
    }

    private static boolean isTLSServerEnabled(SSLSocket sslSocket) {
        System.out.println("__prova__ :: " + sslSocket.getSupportedProtocols().toString());
        for (String protocol : sslSocket.getSupportedProtocols()) {
            if (protocol.equals(TLS_v1_1) || protocol.equals(TLS_v1_2)) {
                return true;
            }
        }
        return false;
    }
}

Edit: Thank's to ademar111190 for the kotlin implementation (link)

class TLSSocketFactory constructor(
        private val internalSSLSocketFactory: SSLSocketFactory
) : SSLSocketFactory() {

    private val protocols = arrayOf("TLSv1.2", "TLSv1.1")

    override fun getDefaultCipherSuites(): Array<String> = internalSSLSocketFactory.defaultCipherSuites

    override fun getSupportedCipherSuites(): Array<String> = internalSSLSocketFactory.supportedCipherSuites

    override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose))

    override fun createSocket(host: String, port: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort))

    override fun createSocket(host: InetAddress, port: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))

    private fun enableTLSOnSocket(socket: Socket?) = socket?.apply {
        if (this is SSLSocket && isTLSServerEnabled(this)) {
            enabledProtocols = protocols
        }
    }

    private fun isTLSServerEnabled(sslSocket: SSLSocket) = sslSocket.supportedProtocols.any { it in protocols }

}

Upvotes: 14

Navneet Krishna
Navneet Krishna

Reputation: 5017

I have some additions to above mentioned answers Its infact a hack mentioned by Jesse Wilson from okhttp, square here. According to this hack, i had to rename my SSLSocketFactory variable to

private SSLSocketFactory delegate;

This is my TLSSocketFactory class

public class TLSSocketFactory extends SSLSocketFactory {

private SSLSocketFactory delegate;

public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    delegate = context.getSocketFactory();
}

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

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

@Override
public Socket createSocket() throws IOException {
    return enableTLSOnSocket(delegate.createSocket());
}

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

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

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

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

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return enableTLSOnSocket(delegate.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;
}
}

and this is how i used it with okhttp and retrofit

 OkHttpClient client=new OkHttpClient();
    try {
        client = new OkHttpClient.Builder()
                .sslSocketFactory(new TLSSocketFactory())
                .build();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

Upvotes: 6

Praveen Kondapalli
Praveen Kondapalli

Reputation: 307

@Inherently Curious - thanks for posting this. You are almost there - you have to add two more params to SSLContext.init() method.

TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() };
sc.init(null, trustManagers, new SecureRandom());

it will start working. Again thank you very much for posting this. I solved this/my issue with your code.

Upvotes: -1

tran minh linh
tran minh linh

Reputation: 211

2 ways to enable TLSv1.1 and TLSv1.2:

  1. use this guideline: http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
  2. use this class https://github.com/erickok/transdroid/blob/master/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java
    schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), port));

Upvotes: 20

Vladimir Mezentsev
Vladimir Mezentsev

Reputation: 918

You should use

 SSLContext.getInstance("TLSv1.2"); 

for specific protocol version.

The second exception occured because default socketFactory used fallback SSLv3 protocol for failures.

You can use NoSSLFactory from main answer here for its suppression How to disable SSLv3 in android for HttpsUrlConnection?

Also you should init SSLContext with all your certificates(client and trusted ones if you need them)

But all of that is useless without using

ProviderInstaller.installIfNeeded(getContext())

Here is more information with proper usage scenario https://developer.android.com/training/articles/security-gms-provider.html

Hope it helps.

Upvotes: 7

Related Questions