google / conscrypt

Conscrypt is a Java Security Provider that implements parts of the Java Cryptography Extension and Java Secure Socket Extension.
Apache License 2.0
1.29k stars 275 forks source link

SSL Exception when downloading html #833

Open LukasAnda opened 4 years ago

LukasAnda commented 4 years ago

Hey guys, I managed to receive this exception:

javax.net.ssl.SSLProtocolException: Read error: ssl=0xd6ccfd88: Failure in SSL library, usually a protocol error 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT (third_party/openssl/boringssl/src/crypto/fipsmodule/cipher/e_aes.c:1040 0xbe8f2c0b:0x00000000) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: error:1000008b:SSL routines:OPENSSL_internal:DECRYPTION_FAILED_OR_BAD_RECORD_MAC (third_party/openssl/boringssl/src/ssl/tls_record.cc:298 0xbe8f2c0b:0x00000000) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at com.google.android.gms.org.conscrypt.NativeCrypto.SSL_read(Native Method) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at com.google.android.gms.org.conscrypt.NativeSsl.read(:com.google.android.gms@200914040@20.09.14 (120700-300565878):3) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at com.google.android.gms.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(:com.google.android.gms@200914040@20.09.14 (120700-300565878):6) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at okio.Okio$2.read(Okio.java:140) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at okio.AsyncTimeout$2.read(AsyncTimeout.java:237) 2020-04-16 19:46:07.404 1265-1382/com.lukasanda.aismobile W/System.err: at okio.RealBufferedSource.read(RealBufferedSource.java:51) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.internal.http1.Http1ExchangeCodec$AbstractSource.read(Http1ExchangeCodec.java:389) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.internal.http1.Http1ExchangeCodec$FixedLengthSource.read(Http1ExchangeCodec.java:427) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.java:286) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okio.RealBufferedSource.read(RealBufferedSource.java:51) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okio.ForwardingSource.read(ForwardingSource.java:35) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:290) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okio.RealBufferedSource.select(RealBufferedSource.java:100) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.internal.Util.bomAwareCharset(Util.java:467) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.ResponseBody.string(ResponseBody.java:181) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at retrofit2.converter.scalars.ScalarResponseBodyConverters$StringResponseBodyConverter.convert(ScalarResponseBodyConverters.java:30) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at retrofit2.converter.scalars.ScalarResponseBodyConverters$StringResponseBodyConverter.convert(ScalarResponseBodyConverters.java:26) 2020-04-16 19:46:07.405 1265-1382/com.lukasanda.aismobile W/System.err: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:227) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:142) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 2020-04-16 19:46:07.407 1265-1382/com.lukasanda.aismobile W/System.err: at java.lang.Thread.run(Thread.java:919)

which traces to here: at com.google.android.gms.org.conscrypt.NativeCrypto.SSL_read(Native Method) Do you have any ideas how to fix this ?

prbprbprb commented 4 years ago

Can you share any more details of the setup?

The actual error here is occurring in BoringSSL, as it decrypts a TLS record the message authentication code (MAC) supplied by the server does not match the one calculated on the client, so the encryption code logs an error causing the TLS layer to also error out.

Possible causes (most likely first):-

  1. The data is being being corrupted in your app, e.g. by multiple threads trying to read from the same TLS connection
  2. The data is getting corrupted in transit, e.g. faulty network hardware or a not-very-good min in the middle attack
  3. A bug in the encryption code on the server, or maybe in BoringSSL but these last two are pretty unlikely. If you can load the same resource in Chrome then it's probably neither of these at fault.
LukasAnda commented 4 years ago

Sure, what details do you need ? I will give you all the details. But basically, I am using coroutines to load some stuff sequentially but I don't think it executes over multiple threads at once.

For now, here is my OkHttp Client setup:

OkHttpClient.Builder().apply {
            dispatcher(Dispatcher(Executors.newFixedThreadPool(1)).apply {
                maxRequests = 1
                maxRequestsPerHost = 1
            })
            connectionPool(ConnectionPool(100, 100, TimeUnit.SECONDS))
            connectionSpecs(
                listOf(
                    ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
                        .supportsTlsExtensions(true)
                        .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
                        .cipherSuites(
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
                            CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
                            CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
                            CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
                        )
                        .build()
                )
            )
            connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            followSslRedirects(true)
            followRedirects(false)
            retryOnConnectionFailure(true)
            addInterceptor(EncodingInterceptor())
            addInterceptor(AuthInterceptor(get()))
            addInterceptor(Interceptor { chain ->
                chain.proceed(chain.request().newBuilder().apply {
                    addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36")
                    addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
                    addHeader("Accept-Encoding", "gzip, deflate, br")
                    addHeader("Accept-Language", "sk-SK,sk;q=0.9,cs;q=0.8,en-US;q=0.7,en;q=0.6")
                    addHeader("Cache-Control", "max-age=0")
                    addHeader("Connection", "keep-alive")
                    addHeader("Host", "is.stuba.sk")
                    addHeader("Origin", "https://is.stuba.sk")
                    addHeader("Upgrade-Insecure-Requests", "1")
                }.build())
            })

            hostnameVerifier(HostnameVerifier { hostname, session -> true })
//            hostnameVerifier { hostname: String?, session: SSLSession? -> true }
            addInterceptor(HttpLoggingInterceptor().apply {
                if (BuildConfig.DEBUG) {
                    level = HttpLoggingInterceptor.Level.BASIC
                }
            })
        }.build() 

Also, if you want to check out more of the code, here it is: repository, on branch feat/documents

LukasAnda commented 4 years ago

@prbprbprb I managed to create a small class that replicates this issue:

class SyncCoroutineWorker2(
    context: Context,
    workerParameters: WorkerParameters
) : CoroutineWorker(context, workerParameters), KoinComponent {

    private val aisApi by inject<AISApi>()

    override suspend fun doWork(): Result = withContext(Dispatchers.Default) {
        val semestersResponse = async { aisApi.semesters().authenticatedOrThrow2() }.await()

        val semesters = Parser.getSemesters(semestersResponse)

//        delay(3000)

        semesters?.forEach { semester ->
            async { aisApi.subjects("${semester.studiesId.ifEmpty { "141967" }};obdobi=${semester.id}").authenticatedOrThrow2() }.await()
        }

        Result.success()
    }
} 

It runs okay the first time, the next time, it just crashes in the middle of the loop. Also I tried without async-await and still the same result