Commit f11bc078 authored by Maxim Myalkin's avatar Maxim Myalkin

Add TLS 1.2 for API < 20.

parent de73573c
package ru.terrakok.gitlabclient.model.data.server
import android.os.Build
import okhttp3.OkHttpClient
import okhttp3.TlsVersion
import timber.log.Timber
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.net.UnknownHostException
import java.security.KeyStore
import javax.net.ssl.*
/**
* Implementation of [SSLSocketFactory] that adds [TlsVersion.TLS_1_2] as an enabled protocol for every [SSLSocket]
* created by [delegate].
*
* [See this discussion for more details.](https://github.com/square/okhttp/issues/2372#issuecomment-244807676)
*
* @see SSLSocket
* @see SSLSocketFactory
*/
class Tls12SocketFactory(private val delegate: SSLSocketFactory) : SSLSocketFactory() {
companion object {
/**
* @return [X509TrustManager] from [TrustManagerFactory]
*
* @throws [NoSuchElementException] if not found. According to the Android docs for [TrustManagerFactory], this
* should never happen because PKIX is the only supported algorithm
*/
private val trustManager by lazy {
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
trustManagerFactory.trustManagers
.first { it is X509TrustManager } as X509TrustManager
}
/**
* If on [Build.VERSION_CODES.LOLLIPOP] or lower, sets [OkHttpClient.Builder.sslSocketFactory] to an instance of
* [Tls12SocketFactory] that wraps the default [SSLContext.getSocketFactory] for [TlsVersion.TLS_1_2].
*
* Does nothing when called on [Build.VERSION_CODES.LOLLIPOP_MR1] or higher.
*
* For some reason, Android supports TLS v1.2 from [Build.VERSION_CODES.JELLY_BEAN], but the spec only has it
* enabled by default from API [Build.VERSION_CODES.KITKAT]. Furthermore, some devices on
* [Build.VERSION_CODES.LOLLIPOP] don't have it enabled, despite the spec saying they should.
*
* @return the (potentially modified) [OkHttpClient.Builder]
*/
@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) {
Timber.e(e, "Error while setting TLS 1.2 compatibility")
}
}
}
}
/**
* Forcefully adds [TlsVersion.TLS_1_2] as an enabled protocol if called on an [SSLSocket]
*
* @return the (potentially modified) [Socket]
*/
private fun Socket.patchForTls12(): Socket {
return (this as? SSLSocket)?.apply {
enabledProtocols += TlsVersion.TLS_1_2.javaName()
} ?: this
}
override fun getDefaultCipherSuites(): Array<String> {
return delegate.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return delegate.supportedCipherSuites
}
@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket? {
return delegate.createSocket(s, host, port, autoClose)
.patchForTls12()
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(host: String, port: Int): Socket? {
return delegate.createSocket(host, port)
.patchForTls12()
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket? {
return delegate.createSocket(host, port, localHost, localPort)
.patchForTls12()
}
@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket? {
return delegate.createSocket(host, port)
.patchForTls12()
}
@Throws(IOException::class)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket? {
return delegate.createSocket(address, port, localAddress, localPort)
.patchForTls12()
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ import ru.terrakok.gitlabclient.entity.TokenData
import ru.terrakok.gitlabclient.entity.User
import ru.terrakok.gitlabclient.entity.app.session.UserAccount
import ru.terrakok.gitlabclient.model.data.server.ServerError
import ru.terrakok.gitlabclient.model.data.server.Tls12SocketFactory.Companion.enableTls12
import ru.terrakok.gitlabclient.model.data.storage.Prefs
import ru.terrakok.gitlabclient.model.system.SchedulersProvider
import ru.terrakok.gitlabclient.toothpick.qualifier.DefaultServerPath
......@@ -21,7 +22,9 @@ class SessionRepository @Inject constructor(
private val schedulers: SchedulersProvider
) {
private val okHttpClient = OkHttpClient()
private val okHttpClient = OkHttpClient.Builder()
.enableTls12()
.build()
fun getCurrentUserAccount(): UserAccount? {
prefs.selectedAccount?.let { id ->
......
......@@ -6,6 +6,7 @@ import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import ru.terrakok.gitlabclient.BuildConfig
import ru.terrakok.gitlabclient.entity.app.session.AuthHolder
import ru.terrakok.gitlabclient.model.data.server.Tls12SocketFactory.Companion.enableTls12
import ru.terrakok.gitlabclient.model.data.server.interceptor.AuthHeaderInterceptor
import ru.terrakok.gitlabclient.model.data.server.interceptor.CurlLoggingInterceptor
import java.util.concurrent.TimeUnit
......@@ -21,6 +22,7 @@ class OkHttpClientProvider @Inject constructor(
) : Provider<OkHttpClient> {
override fun get() = with(OkHttpClient.Builder()) {
enableTls12()
cache(Cache(context.cacheDir, CACHE_SIZE_BYTES))
connectTimeout(TIMEOUT, TimeUnit.SECONDS)
readTimeout(TIMEOUT, TimeUnit.SECONDS)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment