title: try to force app on Android 4.x (API lower than 20) to use TLSv1.2
body: <p>Our backend is updated from SHA-1 &amp; TLS 1.0 to <strong>SHA-2 certificate &amp; TLS 1.2</strong> . It breaks the HTTPS communication of our Android app on platforms with <strong>API level lower than 20</strong>   (<strong>Android 4.1-4.4</strong>).</p>
  
  <p>(Our Android project is using <strong>Retrofit 2.4.0</strong> &amp; <strong>okhttp 3.10.0</strong>)</p>
  
  <p>I try to fix above issue by forcing our app on Android 4.x using <strong>TLS 1.2</strong>, my code is inspired by <a href="https://medium.com/@sreekumar_av/certificate-public-key-pinning-in-android-using-retrofit-2-0-74140800025b" rel="nofollow noreferrer">this tutorial</a> (but I excluded public key pinning of the tutorial code):</p>
  
  <p>I firstly created a <code>TLSSocketFactory</code>:</p>
  
  <pre><code>public class TLSSocketFactory extends SSLSocketFactory {
  
      private SSLSocketFactory internalSSLSocketFactory;
  
      public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
  
          SSLContext sslContext = SSLContext.getInstance("TLS");
          sslContext.init(null, new TrustManager[] { systemDefaultTrustManager() }, null);
          internalSSLSocketFactory = sslContext.getSocketFactory();
      }
  
      @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));
      }
  
      private Socket enableTLSOnSocket(Socket socket) {
          if(socket != null &amp;&amp; (socket instanceof SSLSocket)) {
              ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
          }
          return socket;
      }
  
      public X509TrustManager systemDefaultTrustManager() {
          try {
              TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                      TrustManagerFactory.getDefaultAlgorithm());
              trustManagerFactory.init((KeyStore) null);
              TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
              if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                  throw new IllegalStateException("Unexpected default trust managers:"
                          + Arrays.toString(trustManagers));
              }
              return (X509TrustManager) trustManagers[0];
          } catch (GeneralSecurityException e) {
              throw new AssertionError(); // The system has no TLS. Just give up.
          }
      }
  }
  </code></pre>
  
  <p>Then, apply above <code>TLSSocketFactory</code> to <code>OkHttpClient</code> :</p>
  
  <pre><code>OkHttpClient.Builder builder = new OkHttpClient.Builder();
  
  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();
  
  builder.connectionSpecs(Collections.singletonList(spec));
  
  
  builder.readTimeout(10, TimeUnit.SECONDS);
  builder.connectTimeout(10, TimeUnit.SECONDS);
  builder.writeTimeout(10, TimeUnit.SECONDS);
  
  // apply TLSSocketFactory
  
          try {
              TLSSocketFactory socketFactory = new TLSSocketFactory();
              builder.sslSocketFactory(socketFactory, socketFactory.systemDefaultTrustManager());
          } catch (KeyManagementException e) {
              e.printStackTrace();
          } catch (NoSuchAlgorithmException e) {
              e.printStackTrace();
          }
  
  OkHttpClient client = builder.build();
  </code></pre>
  
  <p>Then, create a instance of <code>Retrofit</code> by using above <code>OkHttpClient</code>:</p>
  
  <pre><code>Retrofit retrofit = new Retrofit.Builder().baseUrl(myUrl)
                  .addConverterFactory(ScalarsConverterFactory.create())
                  .addConverterFactory(GsonConverterFactory.create(gson))
                  .client(client) // my OkHttpClient
                  .build();
  </code></pre>
  
  <p>But when I run my app now on Android 4.x communication to our backend, I still get the <strong>error</strong>:</p>
  
  <pre><code>OkHttp: &lt;-- HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
  </code></pre>
  
  <p>Why? What am I missing?</p>
  
  <p>(In <code>TLSSocketFactory</code>'s constructor, I also tried <code>sslContext.init(null, null, null);</code> but it doesn't help, same error.)</p>
  
  <p>(<strong>The new certificate is fine, it works with Android 5.0+, but new certificate and old certificate are from different issuers</strong>)</p>