Android KitKat does not support TLSv1.2
Asked Answered
V

1

6

I am testing my app on older devices, as we are targeting the developing market and expect users to have older, pre-lollipop, devices.

I use RetroFit (2.6.4) and OkHttp (3.12.4) to communicate with the REST server, which is hosted on Amazon. AWS supports TLS 1.0, 1.1 and 1.2 but we have it locked to TLSv1.2 in our NodeJS server.

The app works for new devices but I get the following error on KitKat:

<-- HTTP FAILED: java.net.UnknownServiceException: Unable to find acceptable protocols. isFallback=false, modes=[ConnectionSpec(cipherSuites=[TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_SHA256, TLS_AES_256_CCM_8_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256], tlsVersions=[TLS_1_3, TLS_1_2], supportsTlsExtensions=true)], supported protocols=[SSLv3, TLSv1, TLSv1.2]

If you look at the end of that error message, it says:

, tlsVersions=[TLS_1_3, TLS_1_2], supportsTlsExtensions=true)], supported protocols=[SSLv3, TLSv1, TLSv1.2]

So I think I have the correct code there to enable TLSv1.2 but I'm not using it correctly, and OKHttp is trying to use SSLv3 or TLS1 instead of TLSv1.2.

Also, I just checked our server on SSLLabs and it supports the following TLSv1.2 cipher suites:

# TLS 1.2 (suites in server-preferred order)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH secp256r1 (eq. 3072 bits RSA)   FS    128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)   ECDH secp256r1 (eq. 3072 bits RSA)   FS   WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)   ECDH secp256r1 (eq. 3072 bits RSA)   FS   WEAK    128
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH secp256r1 (eq. 3072 bits RSA)   FS    256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)   ECDH secp256r1 (eq. 3072 bits RSA)   FS   WEAK 256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)   ECDH secp256r1 (eq. 3072 bits RSA)   FS   WEAK    256
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)   WEAK   128
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c)   WEAK   128
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)   WEAK  128
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)   WEAK   256
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d)   WEAK   256
TLS_RSA_WITH_AES_256_CBC_SHA (0x35)   WEAK  256

Full stacktrace:

java.net.UnknownServiceException: Unable to find acceptable protocols. isFallback=false, modes=[ConnectionSpec(cipherSuites=[TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_SHA256, TLS_AES_256_CCM_8_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256], tlsVersions=[TLS_1_3, TLS_1_2], supportsTlsExtensions=true)], supported protocols=[SSLv3, TLSv1, TLSv1.2]
    at okhttp3.internal.connection.ConnectionSpecSelector.configureSecureSocket(ConnectionSpecSelector.java:74)
    at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:313)
    at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:284)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:169)
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:258)
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:127)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:225)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:250)
    at okhttp3.RealCall.execute(RealCall.java:93)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
    at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:104)

I'm using a custom SocketFactory that I found on this blog post: https://ankushg.com/posts/tls-1.2-on-android/

and using it like so:

fun getHttpClientBuilder(enableLogging: Boolean = BuildConfig.DEBUG, vararg customInterceptors: Interceptor): OkHttpClient.Builder {
    val timeoutSecs = if (BuildConfig.DEBUG) 0 else TIMEOUT_SECONDS_RELEASE
    val httpClientBuilder = OkHttpClient.Builder()
        .connectTimeout(timeoutSecs.toLong(), TimeUnit.SECONDS)
        .readTimeout(timeoutSecs.toLong(), TimeUnit.SECONDS)
        .writeTimeout(timeoutSecs.toLong(), TimeUnit.SECONDS)
        .connectionSpecs(listOf(ConnectionSpec.RESTRICTED_TLS)) //  Restrict to TLS 1.2 and 1.3

    for (customInterceptor in customInterceptors) {
        httpClientBuilder.addInterceptor(customInterceptor)
    }

    val logger = HttpLoggingInterceptor()
    logger.level = if (enableLogging) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.BASIC
    httpClientBuilder.addInterceptor(logger)

    return httpClientBuilder
}

fun buildHttpClient(enableLogging: Boolean = BuildConfig.DEBUG, vararg customInterceptors: Interceptor): OkHttpClient {
    return getHttpClientBuilder(enableLogging, *customInterceptors)
        .enableTls12() //This calls the custom socket factory
        .build()
}

The enableTls12() function call triggers this code:

  @JvmStatic
    fun OkHttpClient.Builder.enableTls12() = apply {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
            try {
                val sslContext = SSLContext.getInstance(TlsVersion.TLS_1_2.javaName())
                sslContext.init(null, arrayOf(trustManager), null)

                sslSocketFactory(Tls12SocketFactory(sslContext.socketFactory), trustManager)
            } catch (e: Exception) {
                println("Error while setting TLS 1.2 compatibility:: ${e.localizedMessage}")
            }
        }
    }

So I think I have everything I need and this should be working. Does anyone have any suggestions as to what I can try or what I'm missing?

Thanks

Vermiculite answered 1/4, 2020 at 12:23 Comment(2)
Did you find a solution to this ?Muniz
@SharpEdge I used Conscrypt with a custom SocketFactory using this TrustManager: github.com/ge0rg/MemorizingTrustManager/tree/master/src/de/…Vermiculite
R
0

Similiar problem to this link below, check on Tls12SocketFactory implementation

Android 4.1 to 4.4 KitKat - Enable TLS 1.2 for API

Raphael answered 1/4, 2020 at 15:44 Comment(1)
Thanks, but if you look in the enableTls12() function, you'll see I'm using a Tls12SocketFactory which is the same one in that link.Vermiculite

© 2022 - 2024 — McMap. All rights reserved.