In readUrlWithCustomCas, use both custom and system CA certificates
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
@@ -48,7 +49,9 @@ import java.net.SocketException
|
|||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Enumeration
|
import java.util.Enumeration
|
||||||
@@ -67,6 +70,7 @@ import javax.crypto.spec.SecretKeySpec
|
|||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.TrustManagerFactory
|
import javax.net.ssl.TrustManagerFactory
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
import kotlin.text.replaceFirstChar
|
import kotlin.text.replaceFirstChar
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
@@ -1008,36 +1012,74 @@ object Utils {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Create a CertificateFactory and load the user's certificate
|
try {
|
||||||
|
// 1. Create a TrustManager that trusts the CAs in the user-provided file
|
||||||
|
val customTrustManager = fun(): X509TrustManager {
|
||||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
val certificateInputStream = caFile.inputStream()
|
val certificateInputStream = caFile.inputStream()
|
||||||
val userCertificate = certificateFactory.generateCertificate(certificateInputStream)
|
// generateCertificates (plural) is crucial for loading all certs from the file
|
||||||
|
val certificates = certificateFactory.generateCertificates(certificateInputStream)
|
||||||
certificateInputStream.close()
|
certificateInputStream.close()
|
||||||
|
|
||||||
// 2. Create a KeyStore containing our trusted CAs
|
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
|
||||||
val keyStoreType = KeyStore.getDefaultType()
|
|
||||||
val keyStore = KeyStore.getInstance(keyStoreType)
|
|
||||||
keyStore.load(null, null)
|
keyStore.load(null, null)
|
||||||
keyStore.setCertificateEntry("user_ca", userCertificate)
|
certificates.forEachIndexed { index, certificate ->
|
||||||
|
keyStore.setCertificateEntry("user_ca_$index", certificate)
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Create a TrustManager that trusts the CAs in our KeyStore
|
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
|
|
||||||
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
|
|
||||||
tmf.init(keyStore)
|
tmf.init(keyStore)
|
||||||
|
return tmf.trustManagers.find { it is X509TrustManager } as X509TrustManager
|
||||||
|
}()
|
||||||
|
|
||||||
// 4. Create an SSLContext that uses our TrustManager
|
// 2. Create a TrustManager that trusts the default system CAs
|
||||||
|
val systemTrustManager = fun(): X509TrustManager {
|
||||||
|
val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
|
factory.init(null as KeyStore?) // A null keystore loads the system's default CAs
|
||||||
|
return factory.trustManagers.find { it is X509TrustManager } as X509TrustManager
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3. Create a composite TrustManager that delegates to both system and custom CAs
|
||||||
|
@SuppressLint("CustomX509TrustManager")
|
||||||
|
val compositeTrustManager = object : X509TrustManager {
|
||||||
|
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
|
||||||
|
// This is for client certificate authentication, which you are not using.
|
||||||
|
// It's safe to just delegate to the system manager by default.
|
||||||
|
systemTrustManager.checkClientTrusted(chain, authType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
|
||||||
|
try {
|
||||||
|
// First, try to validate the chain with the system's default TrustManager.
|
||||||
|
systemTrustManager.checkServerTrusted(chain, authType)
|
||||||
|
} catch (_: CertificateException) {
|
||||||
|
// If that fails, and only if that fails, try to validate with our custom TrustManager.
|
||||||
|
// This will throw the final CertificateException if it also fails, which is the correct behavior.
|
||||||
|
customTrustManager.checkServerTrusted(chain, authType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
||||||
|
// Return a combined list of issuers from both trust managers.
|
||||||
|
return systemTrustManager.acceptedIssuers + customTrustManager.acceptedIssuers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Create an SSLContext that uses our new composite TrustManager
|
||||||
val sslContext = SSLContext.getInstance("TLS")
|
val sslContext = SSLContext.getInstance("TLS")
|
||||||
sslContext.init(null, tmf.trustManagers, null)
|
sslContext.init(null, arrayOf(compositeTrustManager), null)
|
||||||
|
|
||||||
// 5. Tell HttpsURLConnection to use our custom SSLContext
|
// 5. Tell HttpsURLConnection to use our custom SSLContext for this connection
|
||||||
val urlConnection = url.openConnection() as HttpsURLConnection
|
val urlConnection = url.openConnection() as HttpsURLConnection
|
||||||
urlConnection.sslSocketFactory = sslContext.socketFactory
|
urlConnection.sslSocketFactory = sslContext.socketFactory
|
||||||
|
|
||||||
// 6. Proceed with the connection
|
// 6. Proceed with the connection and return the result
|
||||||
return try {
|
return urlConnection.inputStream.bufferedReader().use { it.readText() }
|
||||||
urlConnection.inputStream.bufferedReader().use { it.readText() }
|
|
||||||
} finally {
|
} catch (e: Exception) {
|
||||||
urlConnection.disconnect()
|
// Catch any exception from certificate loading or from the network connection and log it
|
||||||
|
Log.e("Utils", "readUrlWithCustomCa failed: ${e.message}")
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user