Merge branch 'feature/use_rootca_for_sso' into 'release/6.2'

Use liblinphone rootCA.pem file for SSO connection

See merge request BC/public/linphone-android!2472
This commit is contained in:
Sylvain Berfini 2026-04-14 01:03:30 +00:00
commit b13012b653
2 changed files with 63 additions and 3 deletions

View file

@ -491,6 +491,10 @@ class CorePreferences
val messageReceivedInVisibleConversationNotificationSound: String val messageReceivedInVisibleConversationNotificationSound: String
get() = context.filesDir.absolutePath + "/share/sounds/linphone/incoming_chat.wav" get() = context.filesDir.absolutePath + "/share/sounds/linphone/incoming_chat.wav"
@get:AnyThread
val rootCaPem: String
get() = context.filesDir.absolutePath + "/share/linphone/rootca.pem"
@UiThread @UiThread
fun copyAssetsFromPackage() { fun copyAssetsFromPackage() {
copy("linphonerc_default", configPath) copy("linphonerc_default", configPath)

View file

@ -21,10 +21,12 @@ package org.linphone.ui.main.sso.viewmodel
import android.content.Intent import android.content.Intent
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.core.net.toUri
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import java.io.File
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.openid.appauth.AppAuthConfiguration
import net.openid.appauth.AuthState import net.openid.appauth.AuthState
import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationRequest
@ -42,7 +44,19 @@ import org.linphone.ui.GenericViewModel
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.FileUtils import org.linphone.utils.FileUtils
import org.linphone.utils.TimestampUtils import org.linphone.utils.TimestampUtils
import androidx.core.net.toUri import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
class SingleSignOnViewModel class SingleSignOnViewModel
@UiThread @UiThread
@ -134,6 +148,7 @@ class SingleSignOnViewModel
private fun singleSignOn() { private fun singleSignOn() {
Log.i("$TAG Fetch from issuer [$singleSignOnUrl]") Log.i("$TAG Fetch from issuer [$singleSignOnUrl]")
operationInProgress.postValue(true) operationInProgress.postValue(true)
AuthorizationServiceConfiguration.fetchFromIssuer( AuthorizationServiceConfiguration.fetchFromIssuer(
singleSignOnUrl.toUri(), singleSignOnUrl.toUri(),
AuthorizationServiceConfiguration.RetrieveConfigurationCallback { serviceConfiguration, ex -> AuthorizationServiceConfiguration.RetrieveConfigurationCallback { serviceConfiguration, ex ->
@ -177,14 +192,55 @@ class SingleSignOnViewModel
authRequestBuilder.setLoginHint(username) authRequestBuilder.setLoginHint(username)
} }
val appAuthConfig = AppAuthConfiguration.Builder()
.build()
val authRequest = authRequestBuilder.build() val authRequest = authRequestBuilder.build()
authService = AuthorizationService(coreContext.context) authService = AuthorizationService(coreContext.context, appAuthConfig)
val authIntent = authService.getAuthorizationRequestIntent(authRequest) val authIntent = authService.getAuthorizationRequestIntent(authRequest)
startAuthIntentEvent.postValue(Event(authIntent)) startAuthIntentEvent.postValue(Event(authIntent))
},
{ uri ->
val url = URL(uri.toString())
val connection = url.openConnection() as HttpURLConnection
if (connection is HttpsURLConnection) {
val socketFactory = getSocketFactory()
if (socketFactory != null) {
Log.i("$TAG Using custom SSL Socket Factory")
connection.setSSLSocketFactory(socketFactory)
}
}
connection
} }
) )
} }
@WorkerThread
private fun getSocketFactory(): SSLSocketFactory? {
val caPath = corePreferences.rootCaPem
val caInput: InputStream = FileInputStream(caPath)
try {
val ca: Certificate? =
CertificateFactory.getInstance("X.509").generateCertificate(caInput)
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(keyStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, tmf.trustManagers, SecureRandom())
Log.i("$TAG Created custom SSL Socket Factory using rootCa file at [$caPath]")
return sslContext.socketFactory
} catch (ex: Exception) {
Log.e("$TAG Failed to created custom SSL Socket Factory: $ex")
}
return null
}
@UiThread @UiThread
private fun performRefreshToken() { private fun performRefreshToken() {
operationInProgress.postValue(true) operationInProgress.postValue(true)