CertUtil: Move certificate pinning logic to okhttpclientmodule

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-11-06 14:52:47 +05:30
parent 2da2b8663d
commit fc5255ce56
2 changed files with 40 additions and 37 deletions

View File

@@ -20,9 +20,11 @@
package com.aurora.store.data.network
import android.content.Context
import android.util.Base64
import android.util.Log
import com.aurora.store.R
import com.aurora.store.data.model.Algorithm
import com.aurora.store.data.model.ProxyInfo
import com.aurora.store.util.CertUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_ENABLED
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO
@@ -32,12 +34,17 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import java.io.ByteArrayInputStream
import java.io.InputStream
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
import java.net.Authenticator
import java.net.InetSocketAddress
import java.net.PasswordAuthentication
import java.net.Proxy
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@@ -47,6 +54,9 @@ object OkHttpClientModule {
private const val TAG = "HttpClient"
private const val CERT_BEGIN = "-----BEGIN CERTIFICATE-----"
private const val CERT_END = "-----END CERTIFICATE-----"
@Provides
@Singleton
fun providesOkHttpClientInstance(certPinner: CertificatePinner, proxy: Proxy?): OkHttpClient {
@@ -66,7 +76,7 @@ object OkHttpClientModule {
@Singleton
fun providesCertificatePinnerInstance(@ApplicationContext context: Context): CertificatePinner {
// Google needs special handling, see: https://pki.goog/faq/#faq-27
val googleRootCerts = CertUtil.getGoogleRootCertHashes(context).map { "sha256/$it" }
val googleRootCerts = getGoogleRootCertHashes(context).map { "sha256/$it" }
.toTypedArray()
return CertificatePinner.Builder()
@@ -108,4 +118,32 @@ object OkHttpClientModule {
return null
}
}
private fun getGoogleRootCertHashes(context: Context): List<String> {
return try {
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
certs.map {
val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value)
messageDigest.update(it.publicKey.encoded)
Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP)
}
} catch (exception: Exception) {
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
emptyList()
}
}
private fun getX509Certificates(inputStream: InputStream): List<X509Certificate> {
val certificateFactory = CertificateFactory.getInstance("X509")
val rawCerts = inputStream
.bufferedReader()
.use { it.readText() }
.split(CERT_END)
.map { it.substringAfter(CERT_BEGIN).substringBefore(CERT_END).replace("\n", "") }
.filterNot { it.isBlank() }
val decodedCerts = rawCerts.map { Base64.decode(it, Base64.DEFAULT) }
return decodedCerts.map {
certificateFactory.generateCertificate(ByteArrayInputStream(it)) as X509Certificate
}
}
}

View File

@@ -27,22 +27,15 @@ import android.util.Log
import com.aurora.extensions.generateX509Certificate
import com.aurora.extensions.getInstallerPackageNameCompat
import com.aurora.extensions.isPAndAbove
import com.aurora.store.R
import com.aurora.store.data.model.Algorithm
import com.aurora.store.util.PackageUtil.getPackageInfo
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
object CertUtil {
private val TAG = "CertUtil"
private const val CERT_BEGIN = "-----BEGIN CERTIFICATE-----"
private const val CERT_END = "-----END CERTIFICATE-----"
fun isFDroidApp(context: Context, packageName: String): Boolean {
return isInstalledByFDroid(context, packageName) || isSignedByFDroid(context, packageName)
}
@@ -69,20 +62,6 @@ object CertUtil {
}
}
fun getGoogleRootCertHashes(context: Context): List<String> {
return try {
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
certs.map {
val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value)
messageDigest.update(it.publicKey.encoded)
Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP)
}
} catch (exception: Exception) {
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
emptyList()
}
}
private fun isSignedByFDroid(context: Context, packageName: String): Boolean {
return try {
getX509Certificates(context, packageName).any { cert ->
@@ -106,20 +85,6 @@ object CertUtil {
)
}
private fun getX509Certificates(inputStream: InputStream): List<X509Certificate> {
val certificateFactory = CertificateFactory.getInstance("X509")
val rawCerts = inputStream
.bufferedReader()
.use { it.readText() }
.split(CERT_END)
.map { it.substringAfter(CERT_BEGIN).substringBefore(CERT_END).replace("\n", "") }
.filterNot { it.isBlank() }
val decodedCerts = rawCerts.map { Base64.decode(it, Base64.DEFAULT) }
return decodedCerts.map {
certificateFactory.generateCertificate(ByteArrayInputStream(it)) as X509Certificate
}
}
private fun getX509Certificates(context: Context, packageName: String): List<X509Certificate> {
return try {
val packageInfo = getPackageInfoWithSignature(context, packageName)