feature: add ServerUrl

This commit is contained in:
Bogdan Terehov 2024-04-15 21:22:24 +03:00
parent d928433590
commit 9dfd8602ec
7 changed files with 184 additions and 2 deletions

View File

@ -72,7 +72,7 @@ fun BaseExtension.addLibBuildType(
buildConfigField("String", "VERSION_NAME", "\"${versionCatalog.versionName}\"")
if (enableConfig) {
val server = serverType ?: type.serverType
// buildConfigField("ru.template.data.network.ServerUrl", "DEFAULT_SERVER", type.defaultServer)
buildConfigField("ru.template.data.network.ServerUrl", "DEFAULT_SERVER", type.defaultServer)
buildConfigField("String", "DEFAULT_SERVER_TYPE", "\"$server\"")
buildConfigField("Boolean", "ENABLE_SSL_PINNING", type.enableSslPinning.toString())
buildConfigField("Boolean", "ENABLE_LOGS", type.enabledLogs.toString())
@ -132,4 +132,4 @@ sealed class BuildType(
serverType = "Prod",
matchingFallbacks = "release"
)
}
}

View File

@ -0,0 +1,73 @@
import ru.template.data.network.sslpinning.ServerInfo
import ru.template.data.network.sslpinning.UrlInfo
enum class ServerUrl(
val title: String,
val serverType: String,
val serverInfo: ServerInfo
) {
CUSTOMER_PROD(
title = "customerProd",
serverType = "Prod",
ServerInfo(
UrlInfo(
protocol = "https",
url = "customerProd", // TODO change this url
),
pinsHashSet = hashSetOf()
)
),
CUSTOMER_STAGE(
title = "customerStage",
serverType = "Stage",
ServerInfo(
UrlInfo(
protocol = "https",
url = "customerStage", // TODO change this url
),
pinsHashSet = hashSetOf()
)
),
TOUCHIN_MOCK(
title = "touchinMock",
serverType = "Dev",
ServerInfo(
UrlInfo(
protocol = "https",
url = "touchinMock", // TODO change this url
),
pinsHashSet = hashSetOf()
)
),
CUSTOMER_TEST(
title = "customerTest",
serverType = "Test",
ServerInfo(
UrlInfo(
protocol = "https",
url = "customerTest", // TODO change this url
),
pinsHashSet = hashSetOf()
)
);
companion object {
fun fromTitle(title: String?): ServerUrl? = values().associateBy { it.title }[title]
fun fromServerType(type: String?): ServerUrl? = values().associateBy { it.serverType }[type]
}
override fun toString() = title
fun getBaseUrl() = serverInfo.urlInfo.getBaseUrl()
fun getAddress() = serverInfo.urlInfo.getAddress()
fun getId() = ordinal
fun getPins() = serverInfo.getPins()
}

View File

@ -0,0 +1,18 @@
package ru.template.data.network.sslpinning
import ru.template.data.network.storage.EncryptedStorage
data class ServerInfo(
val urlInfo: UrlInfo,
val pinsHashSet: HashSet<String>
) {
fun getPins() = pinsHashSet.toSet()
fun addPins(pinSet: Set<String>) = pinsHashSet.addAll(pinSet)
fun updatePins(baseUrl: String, encryptedStorage: EncryptedStorage) {
with(pinsHashSet) {
addAll(encryptedStorage.getCertificatesSet(baseUrl))
}
}
}

View File

@ -0,0 +1,16 @@
package com.redmadrobot.data.network.sslpinning
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager
object TrustManagerUnsafe: X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) = Unit
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) = Unit
override fun getAcceptedIssuers() = emptyArray<X509Certificate>()
}

View File

@ -0,0 +1,49 @@
package com.redmadrobot.data.network.sslpinning
import android.annotation.SuppressLint
import com.redmadrobot.data.network.NetworkConfig
import com.redmadrobot.domain.extension.toHex
import com.redmadrobot.domain.repository.ssl.SslPublicKeyRepository
import java.security.MessageDigest
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.Locale
import javax.net.ssl.X509TrustManager
@SuppressLint("CustomX509TrustManager")
class TrustManagerWithoutTls(
private val networkConfig: NetworkConfig,
private val sslPublicKeyRepository: SslPublicKeyRepository
) : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) = Unit
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
if (networkConfig.isSslPinningEnabled()) {
chain?.let { checkCertificateFingerprint(it) }
}
}
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
private fun checkCertificateFingerprint(chain: Array<X509Certificate?>) {
val pinFromServer = chain[0]?.let { getSha256FingerprintFormatted(it) }
networkConfig.getCurrentServer().getPins().also {
if (it.contains(pinFromServer)) {
sslPublicKeyRepository.setPublicKey(isValid = true)
return
}
}
sslPublicKeyRepository.setPublicKey(isValid = false)
throw CertificateException("Cannot validate server certificate")
}
private fun getSha256FingerprintFormatted(certificate: X509Certificate): String {
return MessageDigest
.getInstance("SHA-256")
.digest(certificate.encoded)
.toHex(separator = ":").toUpperCase(Locale.getDefault())
}
}

View File

@ -0,0 +1,11 @@
package ru.template.data.network.sslpinning
data class UrlInfo(
private val protocol: String,
private val url: String,
) {
fun getAddress(): String = "$protocol://$url/api/v1/"
fun getBaseUrl(): String = url
}

View File

@ -0,0 +1,15 @@
package ru.template.data.network.storage
import android.content.SharedPreferences
class EncryptedStorage(private val preferences: SharedPreferences) {
fun saveCertificatesToSet(host: String, certificates: Set<String>) =
preferences.edit().putStringSet(host,
getCertificatesSet(host).toMutableSet().apply { addAll(certificates) }.toSet()).apply()
fun getCertificatesSet(host: String): Set<String>
= preferences.getStringSet(host, emptySet()) ?: emptySet()
}