9

Sorry for my english. I try using libruary OKhttp, and i use https for post reqest. Now i have error, when i try post my example, this is error:

java.net.UnknownServiceException: Unable to find acceptable protocols. isFallback=false, modes=[ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA], tlsVersions=[TLS_1_2], supportsTlsExtensions=true)], supported protocols=[SSLv3, TLSv1]

I try fix it, but i cant do this. I dont know what i have this error

And bellow my code:

public class PostOKhttp extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String...ulr) {
            Response response = null;
            OkHttpClient client = new OkHttpClient();
            ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .tlsVersions(TlsVersion.TLS_1_2)
                    .cipherSuites(
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
                    .build();
            client.setConnectionSpecs(Collections.singletonList(spec));

            RequestBody postForm = new FormEncodingBuilder()
                    .add("name", "name")
                    .build();

            Request request = new Request.Builder()
                    .url(ulr[0])
                    .addHeader("id", "--")
                    .addHeader("key", "--")
                    .post(postForm)
                    .build();

            try {
                response = client.newCall(request).execute();
                Log.e("post", response.body().string());

            } catch (Exception e) {
                Log.e("error", e.toString());
            }
            return null;

        }

        @Override
        protected void onPostExecute(String result) {

        }

UDP:

Use CertificatePinner

i add this code

String link = "example.net";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
                    .add(link, "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
                    .add(link, "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
                    .add(link, "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
                    .add(link, "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
                    .build();

            client.setCertificatePinner(certificatePinner);

Now i have this error:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
g8214435
  • 737
  • 2
  • 6
  • 19
  • 1
    So does your backend support TLS1.2 with at least one of the cipher suites you've listed? – laalto Sep 06 '15 at 16:08
  • @laalto if i use TLS_1_0 or TLS_1_1 i have same error – g8214435 Sep 06 '15 at 16:14
  • 1
    The ` supported protocols=[SSLv3, TLSv1]` part of the output suggests your backend only supports ancient TLS1.0 and obsolete SSL3 and none of the ciphersuites you've declared. – laalto Sep 06 '15 at 16:20
  • @laalto if i do like this `.tlsVersions(TlsVersion.SSL_3_0)` i have error `java.security.cert.CertPathValidatorException: Trust anchor for certification path not found` – g8214435 Sep 06 '15 at 16:33
  • 1
    If you're using self-signed certs, use a CertificatePinner to pin them. But do consider upgrading your server to modern TLS instead of reverting to less secure old protocols and ciphers. – laalto Sep 06 '15 at 16:37
  • 1
    @laalto thanks for answer. I update me qestion, now i add `CertificatePinner ` and i have error `SSLHandshakeException` – g8214435 Sep 06 '15 at 17:05

2 Answers2

13

Actually the problem is TLSv1.1 and TLSv1.2 not enabled on Android <5 by default and to connect using these latest secure protocol we must have to enable in Android <5 devices.

Because by default android device pick the highest supported protocol to establish the connection but the highest/ newest secure protocol (eg. TLSV1.1 or TLSV1.2) are not enable by default (only enabled are SSLV3.0 or TLSV1.0).

Enabling the TLSV1.1 and TLSV1.2 in android < 5

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/**
 * @author Bajrang Hudda
 */

    public class MyTLSSocketFactory extends SSLSocketFactory {

        private SSLSocketFactory internalSSLSocketFactory;

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

And now add it in your Okhttpclient -

 protected static OkHttpClient getHttpClient(long timeout){

        String hostname = Constants.HOST_NAME_DEBUG;
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
                .add(hostname, "sha1/mBN/TTGneHe2Hq0yFG+SRt5nMZQ=")
                .add(hostname, "sha1/6CgvsAgBlX3PYiYRGedC0NZw7ys=")
                .build();

        //specifying the specs; this is impotent otherwise android <5 won't work 
        //And do note to include the android < 5 supported specs.
        ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                .tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2)
                .cipherSuites(
                        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                        CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
                .build();

        final OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.setCertificatePinner(certificatePinner);
        okHttpClient.setConnectionSpecs(Collections.singletonList(spec));
        try
        {
            // enabling the tlsv1.1 and tlsv.2
            okHttpClient.setSslSocketFactory(new MyTLSSocketFactory()); 
        } catch (KeyManagementException e)
        {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        return okHttpClient;
    }

And now finally add it in your retrofit -

 RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(Constants.API_URL)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setErrorHandler(new ErrorHandler())
                .setClient(getHttpClient())
                .setRequestInterceptor(new SecureHeaderInterceptor(null))
                .build();

That's it, Happy Coding :-)

Bajrang Hudda
  • 3,028
  • 1
  • 36
  • 63
6

You need to use ProviderInstaller to check and install if needed TLS support. Use something like this on your entry Activity and it should do the trick. Check more info here

protected void checkTls() {
    if (android.os.Build.VERSION.SDK_INT < 21) {
        try {
            ProviderInstaller.installIfNeededAsync(this, new ProviderInstaller.ProviderInstallListener() {
                @Override
                public void onProviderInstalled() {
                    SSLContext sslContext = null;
                    try {
                        sslContext = SSLContext.getInstance("TLSv1.2");
                        sslContext.init(null, null, null);
                        SSLEngine engine = sslContext.createSSLEngine();
                    } catch (NoSuchAlgorithmException e) {
                        e.printStackTrace();
                    } catch (KeyManagementException e) {
                        e.printStackTrace();
                    }

                }

                @Override
                public void onProviderInstallFailed(int i, Intent intent) {
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
manuelvsc
  • 525
  • 7
  • 11