Explicitly integrate Conscrypt (#1796)

* Merge HttpClient and HttpClientBuilder

- Remove `HttpClient` class and replace with `OkHttpClient`
- Update all references to `HttpClient` to use `OkHttpClient`
- Add new `HttpClientBuilder` class for building `OkHttpClient` instances
- Update all builder usages to use the new `HttpClientBuilder` class

* KDoc

* Integrate Conscrypt for TLS

- Add Conscrypt dependency
- Initialize Conscrypt in HttpClientBuilder
- Create ConscryptIntegration utility

* KDoc

* Make object a class, better test

* Update cert4android to the latest version (doesn't bundle Conscrypt anymore)
This commit is contained in:
Ricki Hirner
2025-11-06 10:07:40 +01:00
committed by GitHub
parent a8bd296520
commit ee098c4a83
5 changed files with 96 additions and 3 deletions

View File

@@ -186,7 +186,7 @@ dependencies {
}
// third-party libs
@Suppress("RedundantSuppression")
implementation(libs.conscrypt)
implementation(libs.dnsjava)
implementation(libs.guava)
implementation(libs.mikepenz.aboutLibraries.m3)

View File

@@ -0,0 +1,31 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.network
import org.conscrypt.Conscrypt
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import java.security.Security
class ConscryptIntegrationTest {
val integration = ConscryptIntegration()
@Test
fun testInitialize_InstallsConscrypt() {
uninstallConscrypt()
assertFalse(integration.conscryptInstalled())
integration.initialize()
assertTrue(integration.conscryptInstalled())
}
private fun uninstallConscrypt() {
for (conscrypt in Security.getProviders().filter { Conscrypt.isConscrypt(it) })
Security.removeProvider(conscrypt.name)
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/
package at.bitfire.davdroid.network
import androidx.annotation.VisibleForTesting
import org.conscrypt.Conscrypt
import java.security.Security
import java.util.logging.Logger
import javax.net.ssl.SSLContext
/**
* Integration with the Conscrypt library that provides recent TLS versions and ciphers,
* regardless of the device Android version.
*/
class ConscryptIntegration {
private val logger
get() = Logger.getLogger(javaClass.name)
private var initialized = false
/**
* Loads and initializes Conscrypt (if not already done). Safe to be called multiple times.
*/
fun initialize() {
synchronized(ConscryptIntegration::javaClass) {
if (initialized)
return
val alreadyInstalled = conscryptInstalled()
if (!alreadyInstalled) {
// install Conscrypt as most preferred provider
Security.insertProviderAt(Conscrypt.newProvider(), 1)
val version = Conscrypt.version()
logger.info("Using Conscrypt/${version.major()}.${version.minor()}.${version.patch()} for TLS")
val engine = SSLContext.getDefault().createSSLEngine()
logger.info("Enabled protocols: ${engine.enabledProtocols.joinToString(", ")}")
logger.info("Enabled ciphers: ${engine.enabledCipherSuites.joinToString(", ")}")
}
initialized = true
}
}
@VisibleForTesting
internal fun conscryptInstalled() =
Security.getProviders().any { Conscrypt.isConscrypt(it) }
}

View File

@@ -44,7 +44,7 @@ import javax.net.ssl.SSLContext
* Builder for the [OkHttpClient].
*
* **Attention:** If the builder is injected, it shouldn't be used from multiple locations to generate different clients because then
* there's only one [Builder] object and setting properties from one location would influence the others.
* there's only one [HttpClientBuilder] object and setting properties from one location would influence the others.
*
* To generate multiple clients, inject and use `Provider<HttpClientBuilder>` instead.
*/
@@ -58,6 +58,13 @@ class HttpClientBuilder @Inject constructor(
private val settingsManager: SettingsManager
) {
companion object {
init {
// make sure Conscrypt is available when the HttpClientBuilder class is loaded the first time
ConscryptIntegration().initialize()
}
}
/**
* Flag to prevent multiple [build] calls
*/