Enable TLS 1.1 and 1.2 in Android through OkhttpClient
TLS1.1 and TLS1.2 is not enabled in Android by default for API 19 or below. If an app is running on Device running Android API 19 or older and trying to make REST request to a server that requires TLS1.1 or above, it will fail.
Checking what versions a server is supporting can be done with nmap, this nmap command line will reveal that.
nmap -sV --script ssl-enum-ciphers github.com
The result of the above should list the TLS versions github supports, as you can see, there is only TlS1.2 is listed from the result as of 11/4/2019, which means Github only supports TLS1.2
Let’s create a simple app making a REST call to github api, here is a post does exactly that.
Then run the app on a device running on API 19 or older, it will fail to get any results from the github REST service because the app is using TLS1.0 and Github requires TLS1.2.
Let’s create a socket factory class to enable TLS1.1 and TLS1.2, TlsSocketFactory.kt
/** * Enables TLSv1.1 and TLSv1.2 when creating SSLSockets. * References: * https://stackoverflow.com/questions/28943660/how-to-enable-tls-1-2-support-in-an-android-application-running-on-android-4-1 * https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/ * https://ankushg.com/posts/tls-1.2-on-android/ * https://github.com/square/okhttp/issues/2372#issuecomment-244807676 * * Android does not support TLS1.1 and TLS1.2 for API 19 or below. * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket#protocols * @see SSLSocketFactory */ class TlsSocketFactory(private val delegate: SSLSocketFactory) : SSLSocketFactory() { companion object { val ALLOWED_TLS_VERSIONS = arrayOf(TlsVersion.TLS_1_1.javaName(), TlsVersion.TLS_1_2.javaName()) } override fun getDefaultCipherSuites(): Array{ return delegate.defaultCipherSuites } override fun getSupportedCipherSuites(): Array { return delegate.supportedCipherSuites } @Throws(IOException::class) override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket? { return patch(delegate.createSocket(s, host, port, autoClose)) } @Throws(IOException::class, UnknownHostException::class) override fun createSocket(host: String, port: Int): Socket? { return patch(delegate.createSocket(host, port)) } @Throws(IOException::class, UnknownHostException::class) override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket? { return patch(delegate.createSocket(host, port, localHost, localPort)) } @Throws(IOException::class) override fun createSocket(host: InetAddress, port: Int): Socket? { return patch(delegate.createSocket(host, port)) } @Throws(IOException::class) override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket? { return patch(delegate.createSocket(address, port, localAddress, localPort)) } private fun patch(s: Socket): Socket { if (s is SSLSocket) { s.enabledProtocols = ALLOWED_TLS_VERSIONS } return s } }
Now, build a OkhttpClient with the above socket factory to enable TLS1.1 and TLS1.2 to all REST calls that’s going through Okhttp. The important lines here are val httpClientBuilder = createOkhttpClientBuilderWithTlsConfig()
and val httpClient = httpClientBuilder.addInterceptor(interceptor).build()
and the important function is createOkhttpClientBuilderWithTlsConfig()
class RestUtil private constructor() { companion object { private val API_BASE_URL = "https://api.github.com/" private var self: RestUtil? = null val instance: RestUtil get() { if (self == null) { synchronized(RestUtil::class.java) { if (self == null) { self = RestUtil() } } } return self!! } } val retrofit: Retrofit init { val interceptor = HttpLoggingInterceptor() interceptor.level = HttpLoggingInterceptor.Level.BODY val httpClientBuilder = createOkhttpClientBuilderWithTlsConfig() val httpClient = httpClientBuilder.addInterceptor(interceptor).build() val builder = Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(CoroutineCallAdapterFactory()) retrofit = builder.client(httpClient).build() } private fun createOkhttpClientBuilderWithTlsConfig(): OkHttpClient.Builder { return OkHttpClient.Builder().apply { val trustManager by lazy { val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(null as KeyStore?) trustManagerFactory.trustManagers.first { it is X509TrustManager } as X509TrustManager } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { try { val sc = SSLContext.getInstance(TlsVersion.TLS_1_1.javaName()) sc.init(null, null, null) sslSocketFactory(TlsSocketFactory(sc.socketFactory), trustManager) val cs = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(*TlsSocketFactory.ALLOWED_TLS_VERSIONS) .build() val specs = ArrayList() specs.add(cs) specs.add(ConnectionSpec.COMPATIBLE_TLS) specs.add(ConnectionSpec.CLEARTEXT) connectionSpecs(specs) } catch (exc: Exception) { Log.e("OkHttpTLSCompat", "Error while setting TLS 1.1 and 1.2", exc) } } } } }
https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
https://ankushg.com/posts/tls-1.2-on-android/
https://github.com/square/okhttp/issues/2372#issuecomment-244807676
Search within Codexpedia
Search the entire web