Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dokka = "2.0.0"
junit = "4.13.2"
kotlin = "2.2.0"
mockk = "1.14.4"
okhttp3 = "5.1.0"
okhttp = "5.3.0"

[libraries]
androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
Expand All @@ -36,7 +36,8 @@ conscrypt = { module = "org.conscrypt:conscrypt-android", version.ref = "conscry
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp3" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver3", version.ref = "okhttp" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
1 change: 1 addition & 0 deletions lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ android {
packaging {
resources {
excludes.add("META-INF/*.md")
excludes.add("META-INF/versions/*/OSGI-INF/MANIFEST.MF")
}
}

Expand Down
5 changes: 3 additions & 2 deletions sample-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,17 @@ android {
dependencies {
implementation(libs.androidx.core)
implementation(libs.androidx.lifecycle.runtime)

implementation(libs.androidx.appcompat)

implementation(libs.androidx.activityCompose)

implementation(platform(libs.compose.bom))
implementation(libs.compose.material3)
implementation(libs.compose.ui.base)
implementation(libs.compose.ui.graphics)
debugImplementation(libs.compose.ui.toolingPreview)
implementation(libs.compose.runtime.livedata)

implementation(libs.okhttp)

implementation(project(":lib"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package at.bitfire.cert4android.demo
import android.Manifest
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import android.net.SSLCertificateSocketFactory
import android.os.Build
import android.os.Bundle
import android.util.Log
Expand Down Expand Up @@ -37,10 +37,12 @@ import at.bitfire.cert4android.ThemeManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.internal.tls.OkHostnameVerifier
import org.apache.http.conn.ssl.AllowAllHostnameVerifier
import org.apache.http.conn.ssl.StrictHostnameVerifier
import java.net.URL
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext

class MainActivity : ComponentActivity() {

Expand All @@ -66,15 +68,15 @@ class MainActivity : ComponentActivity() {
}

Button(onClick = {
model.testAccess("https://www.github.com")
model.testAccess("https://icloud.com")
}, modifier = Modifier.padding(top = 16.dp)) {
Text("Access normal URL with trusted system certs")
Text("Access icloud.com with trusted system certs")
}

Button(onClick = {
model.testAccess("https://www.github.com", trustSystemCerts = false)
model.testAccess("https://icloud.com", trustSystemCerts = false)
}, modifier = Modifier.padding(top = 16.dp)) {
Text("Access normal URL with distrusted system certs")
Text("Access icloud.com with distrusted system certs")
}

Button(onClick = {
Expand Down Expand Up @@ -125,11 +127,8 @@ class MainActivity : ComponentActivity() {

class Model(application: Application): AndroidViewModel(application) {

companion object {

const val TAG = "cert4android.sample-app"

}
val context: Context
get() = getApplication()

val appInForeground = MutableStateFlow(true)
val resultMessage = MutableLiveData<String>()
Expand All @@ -141,7 +140,7 @@ class MainActivity : ComponentActivity() {
}

fun reset() = viewModelScope.launch(Dispatchers.IO) {
CustomCertStore.getInstance(getApplication()).clearUserDecisions()
CustomCertStore.getInstance(context).clearUserDecisions()
}

fun setInForeground(foreground: Boolean) {
Expand All @@ -150,31 +149,57 @@ class MainActivity : ComponentActivity() {

fun testAccess(url: String, trustSystemCerts: Boolean = true) = viewModelScope.launch(Dispatchers.IO) {
try {
val urlConn = URL(url).openConnection() as HttpsURLConnection
val client = buildClient(trustSystemCerts)

// set cert4android TrustManager and HostnameVerifier
val certMgr = CustomCertManager(
getApplication(),
trustSystemCerts = trustSystemCerts,
appInForeground = appInForeground
// access sample URL
val call = client.newCall(
Request.Builder()
.get()
.url(url)
.build()
)
urlConn.hostnameVerifier = certMgr.HostnameVerifier(StrictHostnameVerifier())
urlConn.sslSocketFactory = object : SSLCertificateSocketFactory(/* handshakeTimeoutMillis = */ 1000) {
init {
setTrustManagers(arrayOf(certMgr))
}
call.execute().use { response ->
// log result
Log.i(TAG, "testAccess(): HTTP ${response.code}")
resultMessage.postValue("${response.code} ${response.message}")
}

// access sample URL
Log.i(TAG, "testAccess(): HTTP ${urlConn.responseCode}")
resultMessage.postValue("${urlConn.responseCode} ${urlConn.responseMessage}")
urlConn.inputStream.close()
} catch (e: Exception) {
resultMessage.postValue("testAccess() ERROR: ${e.message}")
Log.w(TAG, "testAccess(): ERROR: ${e.message}")
}
}

fun buildClient(trustSystemCerts: Boolean): OkHttpClient {
val builder = OkHttpClient.Builder()

// set cert4android TrustManager and HostnameVerifier
val certManager = CustomCertManager(
context,
trustSystemCerts = trustSystemCerts,
appInForeground = appInForeground
)

val sslContext = SSLContext.getInstance("TLS")
sslContext.init(
/* km = */ null,
/* tm = */ arrayOf(certManager),
/* random = */ null
)
builder
.sslSocketFactory(sslContext.socketFactory, certManager)
.hostnameVerifier(certManager.HostnameVerifier(OkHostnameVerifier))

// add cache
//builder.cache(Cache(context.cacheDir, 10 * 1024 * 1024))

return builder.build()
}

}


companion object {
const val TAG = "cert4android.sample-app"
}

}
Loading