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 package com.aurora.store.data.network
import android.content.Context import android.content.Context
import android.util.Base64
import android.util.Log 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.data.model.ProxyInfo
import com.aurora.store.util.CertUtil
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_ENABLED import com.aurora.store.util.Preferences.PREFERENCE_PROXY_ENABLED
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO
@@ -32,12 +34,17 @@ import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import java.io.ByteArrayInputStream
import java.io.InputStream
import okhttp3.CertificatePinner import okhttp3.CertificatePinner
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.net.Authenticator import java.net.Authenticator
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.PasswordAuthentication import java.net.PasswordAuthentication
import java.net.Proxy import java.net.Proxy
import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Singleton import javax.inject.Singleton
@@ -47,6 +54,9 @@ object OkHttpClientModule {
private const val TAG = "HttpClient" private const val TAG = "HttpClient"
private const val CERT_BEGIN = "-----BEGIN CERTIFICATE-----"
private const val CERT_END = "-----END CERTIFICATE-----"
@Provides @Provides
@Singleton @Singleton
fun providesOkHttpClientInstance(certPinner: CertificatePinner, proxy: Proxy?): OkHttpClient { fun providesOkHttpClientInstance(certPinner: CertificatePinner, proxy: Proxy?): OkHttpClient {
@@ -66,7 +76,7 @@ object OkHttpClientModule {
@Singleton @Singleton
fun providesCertificatePinnerInstance(@ApplicationContext context: Context): CertificatePinner { fun providesCertificatePinnerInstance(@ApplicationContext context: Context): CertificatePinner {
// Google needs special handling, see: https://pki.goog/faq/#faq-27 // 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() .toTypedArray()
return CertificatePinner.Builder() return CertificatePinner.Builder()
@@ -108,4 +118,32 @@ object OkHttpClientModule {
return null 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.generateX509Certificate
import com.aurora.extensions.getInstallerPackageNameCompat import com.aurora.extensions.getInstallerPackageNameCompat
import com.aurora.extensions.isPAndAbove import com.aurora.extensions.isPAndAbove
import com.aurora.store.R
import com.aurora.store.data.model.Algorithm import com.aurora.store.data.model.Algorithm
import com.aurora.store.util.PackageUtil.getPackageInfo import com.aurora.store.util.PackageUtil.getPackageInfo
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
object CertUtil { object CertUtil {
private val TAG = "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 { fun isFDroidApp(context: Context, packageName: String): Boolean {
return isInstalledByFDroid(context, packageName) || isSignedByFDroid(context, packageName) 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 { private fun isSignedByFDroid(context: Context, packageName: String): Boolean {
return try { return try {
getX509Certificates(context, packageName).any { cert -> 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> { private fun getX509Certificates(context: Context, packageName: String): List<X509Certificate> {
return try { return try {
val packageInfo = getPackageInfoWithSignature(context, packageName) val packageInfo = getPackageInfoWithSignature(context, packageName)