diff --git a/buildSrc/src/main/kotlin/BuildType.kt b/buildSrc/src/main/kotlin/BuildType.kt index 33ad92c..49251c1 100644 --- a/buildSrc/src/main/kotlin/BuildType.kt +++ b/buildSrc/src/main/kotlin/BuildType.kt @@ -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" ) -} \ No newline at end of file +} diff --git a/data/src/main/java/ru/template/data/network/ServerUrl.kt b/data/src/main/java/ru/template/data/network/ServerUrl.kt new file mode 100644 index 0000000..47ff727 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/ServerUrl.kt @@ -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() + +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt b/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt new file mode 100644 index 0000000..01524d5 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt @@ -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 +) { + fun getPins() = pinsHashSet.toSet() + + fun addPins(pinSet: Set) = pinsHashSet.addAll(pinSet) + + fun updatePins(baseUrl: String, encryptedStorage: EncryptedStorage) { + with(pinsHashSet) { + addAll(encryptedStorage.getCertificatesSet(baseUrl)) + } + } +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt new file mode 100644 index 0000000..02ce243 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt @@ -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?, authType: String?) = Unit + + @Throws(CertificateException::class) + override fun checkServerTrusted(chain: Array?, authType: String?) = Unit + + override fun getAcceptedIssuers() = emptyArray() +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt new file mode 100644 index 0000000..92d52ca --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt @@ -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?, authType: String?) = Unit + + override fun checkServerTrusted(chain: Array?, authType: String?) { + if (networkConfig.isSslPinningEnabled()) { + chain?.let { checkCertificateFingerprint(it) } + } + } + + override fun getAcceptedIssuers(): Array = emptyArray() + + private fun checkCertificateFingerprint(chain: Array) { + 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()) + } + +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt b/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt new file mode 100644 index 0000000..08d9570 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt @@ -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 + +} diff --git a/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt b/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt new file mode 100644 index 0000000..d51da41 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt @@ -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) = + preferences.edit().putStringSet(host, + getCertificatesSet(host).toMutableSet().apply { addAll(certificates) }.toSet()).apply() + + + fun getCertificatesSet(host: String): Set + = preferences.getStringSet(host, emptySet()) ?: emptySet() + +}