Ktor: javax.net.ssl.SSLException crash on Android with okhttp client

Created on 12 Feb 2019  路  4Comments  路  Source: ktorio/ktor

Ktor Version

1.1.2

Ktor Engine Used(client or server and name)

ktor-client-okhttp 1.1.2

JVM Version, Operating System and Relevant Context

Android 4.2.2

Feedback

Caught this crash accidentally, while performing request:

02-10 15:25:12.044 14612-14628/com.example.android.kotlincoroutines D/FA: Connected to remote service
02-10 15:25:12.046 14612-14628/com.example.android.kotlincoroutines V/FA: Processing queued up service tasks: 4
02-10 15:25:12.114 14612-14612/com.example.android.kotlincoroutines I/InputMethodManager: handleMessage: MSG_SET_ACTIVE true, was false
02-10 15:25:13.890 14612-14637/com.example.android.kotlincoroutines I/SurfaceTextureClient: [STC::queueBuffer] (this:0x4e60dab8) fps:12.93, dur:1856.73, max:1397.50, min:7.01
02-10 15:25:16.975 14612-14640/com.example.android.kotlincoroutines I/System.out: [socket][/192.168.1.183:46401] connected
02-10 15:25:16.978 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
02-10 15:25:16.979 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
02-10 15:25:16.981 14612-14640/com.example.android.kotlincoroutines I/dalvikvm: Could not find method java.nio.file.Files.newOutputStream, referenced from method okio.Okio.sink
02-10 15:25:16.982 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to resolve static method 350: Ljava/nio/file/Files;.newOutputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream;
02-10 15:25:16.982 14612-14640/com.example.android.kotlincoroutines D/dalvikvm: VFY: replacing opcode 0x71 at 0x0002
02-10 15:25:16.985 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
02-10 15:25:16.986 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
02-10 15:25:16.987 14612-14640/com.example.android.kotlincoroutines I/dalvikvm: Could not find method java.nio.file.Files.newInputStream, referenced from method okio.Okio.source
02-10 15:25:16.988 14612-14640/com.example.android.kotlincoroutines W/dalvikvm: VFY: unable to resolve static method 349: Ljava/nio/file/Files;.newInputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;
02-10 15:25:16.988 14612-14640/com.example.android.kotlincoroutines D/dalvikvm: VFY: replacing opcode 0x71 at 0x0002
02-10 15:25:17.003 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 NativeCrypto_SSL_do_handshake fd=0x1ef00005 shc=0x1f000009 timeout_millis=60000 client_mode=1 npn=0x0
02-10 15:25:17.004 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: doing handshake ++
02-10 15:25:17.004 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback where=0x10 ret=1
02-10 15:25:17.004 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 handshake start in UNKWN  before/connect initialization
02-10 15:25:17.005 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback ignored
02-10 15:25:17.005 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback where=0x1001 ret=1
02-10 15:25:17.006 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 SSL_connect:UNKWN  before/connect initialization
02-10 15:25:17.006 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback ignored
02-10 15:25:17.007 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback where=0x1001 ret=1
02-10 15:25:17.007 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 SSL_connect:23WCHA SSLv2/v3 write client hello A
02-10 15:25:17.007 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback ignored
02-10 15:25:17.008 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback where=0x1002 ret=-1
02-10 15:25:17.008 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 SSL_connect:error exit in 23RSHA SSLv2/v3 read server hello A
02-10 15:25:17.008 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback ignored
02-10 15:25:17.008 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: doing handshake -- ret=-1
02-10 15:25:17.009 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 NativeCrypto_SSL_do_handshake ret=-1 errno=11 sslError=2 timeout_millis=60000
02-10 15:25:17.009 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: sslSelect type=READ fd=59 appData=0x4f095528 timeout_millis=60000
02-10 15:25:17.017 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: sslSelect READ fd=59 appData=0x4f095528 timeout_millis=60000 => 1
02-10 15:25:17.017 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: doing handshake ++
02-10 15:25:17.017 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback where=0x1002 ret=-1
02-10 15:25:17.017 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 SSL_connect:error exit in 23RSHA SSLv2/v3 read server hello A
02-10 15:25:17.018 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 info_callback ignored
02-10 15:25:17.018 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: doing handshake -- ret=-1
02-10 15:25:17.018 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 NativeCrypto_SSL_do_handshake ret=-1 errno=104 sslError=5 timeout_millis=60000
02-10 15:25:17.018 14612-14640/com.example.android.kotlincoroutines E/NativeCrypto: Unknown error during handshake
02-10 15:25:17.019 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 NativeCrypto_SSL_do_handshake unclean error => 0
02-10 15:25:17.019 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x4f078a38 NativeCrypto_SSL_interrupt
02-10 15:25:17.020 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto:  sslNotify, appData=0x4f095528 ret=1
02-10 15:25:17.020 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto:  sslNotify, appData=0x4f095528 ret=1
02-10 15:25:17.021 14612-14640/com.example.android.kotlincoroutines I/System.out: [CDS]close[46401]
02-10 15:25:17.022 14612-14640/com.example.android.kotlincoroutines I/System.out: close [socket][/0.0.0.0:46401]
02-10 15:25:17.022 14612-14640/com.example.android.kotlincoroutines D/NativeCrypto: ssl=0x0 NativeCrypto_SSL_interrupt
02-10 15:25:17.023 14612-14640/com.example.android.kotlincoroutines I/System.out: close [socket][/0.0.0.0:46401]
02-10 15:25:17.065 14612-14612/com.example.android.kotlincoroutines E/AndroidRuntime: FATAL EXCEPTION: main
    javax.net.ssl.SSLException: SSL handshake aborted: ssl=0x4f078a38: I/O error during system call, Connection reset by peer
        at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java)
        at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:318)
        at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:282)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:167)
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
        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:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:147)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java)
        at java.lang.Thread.run(Thread.java)
02-10 15:25:20.087 14612-14612/com.example.android.kotlincoroutines I/Process: Sending signal. PID: 14612 SIG: 9

All 4 comments

Hi @gabin8, thanks for the report.
Could you provide the request URL? It looks like the problem in server certificates.

@e5l
The urls:
https://tls-v1-1.badssl.com:1011/
https://tls-v1-2.badssl.com:1012/

The issue is related to TLS support on Android 4.x.
Android supports TLS v1.1 and v1.2 from API 16, but enables it by default only from API 20.

I followed the article to write extension function for OkHttpBuilder to enable Tls 1.1-1.2 support
Here's the gist of extension file https://gist.github.com/gabin8/9459d0a00ffdf40ed023a0cf85a5d58e

But ktor doesn't work either. The initialization code:

private val client: HttpClient by lazy {
        HttpClient {
            engine {
                OkHttp.create {
                    config {
                        OkHttpClient.Builder()
                                .followRedirects(true)
                                .followSslRedirects(true)
                                .retryOnConnectionFailure(true)
                                .cache(null)
                                .connectTimeout(30, TimeUnit.SECONDS)
                                .readTimeout(30, TimeUnit.SECONDS)
                                .writeTimeout(30, TimeUnit.SECONDS)
                                .enableTlsOnPreLollipop()
                                .build()
                    }
                }
            }

        }
    }

private suspend fun makeRequest(url: String) {
        try {
            Log.d(TAG, "Started loading $url")
            val content = client.get<String>(url)
            Log.d(TAG, "Finished loading $url")
            responseLiveData.postValue(content)
        } catch (e: Exception) {
            Log.d(TAG, "Got exception for $url: ${e.message}")
            e.printStackTrace()
            responseLiveData.postValue(e.message)
        }
    }

To check that my extension function works fine I've wrote the method using OkHttp library

    private fun makeOkHttpRequest(url: String) {
        val client = OkHttpClient.Builder()
                .followRedirects(true)
                .followSslRedirects(true)
                .retryOnConnectionFailure(true)
                .cache(null)
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .enableTlsOnPreLollipop()
                .build()
        val request = Request.Builder()
                .url(url)
                .build()
        client.newCall(request).enqueue(object: Callback {
            override fun onFailure(call: Call, e: IOException) {
            }

            override fun onResponse(call: Call, response: Response) {
                responseLiveData.postValue(response.body()?.string())
            }
        })
    }

So both snippets are similar , but ktor doesn't work in that case

Still present in 1.1.3
I've created test project that reproduces the issue https://github.com/gabin8/KtorBug946
The video of the bug: https://www.dropbox.com/s/d5sj3q4tk46qglq/ktor_bug_903.mp4?dl=0

It seems that SSLSocketFactory isn't called in ktor-okhttp client at all

The bug is not longer valid, because since version 1.2.0 ktor uses OkHttp client that is no longer available for Android 4.x (okhttp 3.13+)
The OkHttp 3.12.x branch supports Android 2.3+ (API level 9+) and Java 7+

Was this page helpful?
0 / 5 - 0 ratings