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