[feat] Optional self signed certificate
This commit is contained in:
@@ -1,30 +1,27 @@
|
||||
|
||||
package com.acitelight.aether.service
|
||||
|
||||
import android.content.Context
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.CertificatePinner
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.InetAddress
|
||||
import java.net.URL
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import okhttp3.EventListener
|
||||
import java.net.InetAddress
|
||||
import android.util.Log
|
||||
import okhttp3.ConnectionSpec
|
||||
|
||||
object ApiClient {
|
||||
var base: String = ""
|
||||
@@ -34,6 +31,14 @@ object ApiClient {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
private val dnsEventListener = object : EventListener() {
|
||||
override fun dnsEnd(call: okhttp3.Call, domainName: String, inetAddressList: List<InetAddress>) {
|
||||
super.dnsEnd(call, domainName, inetAddressList)
|
||||
val ipAddresses = inetAddressList.joinToString(", ") { it.hostAddress }
|
||||
Log.d("OkHttp_DNS", "Domain '$domainName' resolved to IPs: [$ipAddresses]")
|
||||
}
|
||||
}
|
||||
|
||||
fun loadCertificateFromString(pemString: String): X509Certificate {
|
||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||
val decodedPem = pemString
|
||||
@@ -108,6 +113,7 @@ object ApiClient {
|
||||
}
|
||||
|
||||
return OkHttpClient.Builder()
|
||||
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
|
||||
.sslSocketFactory(sslContext.socketFactory, combinedTm)
|
||||
.build()
|
||||
|
||||
@@ -116,13 +122,20 @@ object ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
fun createOkHttp(cert: String?): OkHttpClient {
|
||||
val trustedCert = cert?.let { loadCertificateFromString(it) }
|
||||
return createOkHttpClientWithDynamicCert(trustedCert)
|
||||
fun createOkHttp(): OkHttpClient {
|
||||
return if (cert == "")
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
|
||||
.eventListener(dnsEventListener)
|
||||
.build()
|
||||
else
|
||||
createOkHttpClientWithDynamicCert(loadCertificateFromString(cert))
|
||||
|
||||
}
|
||||
|
||||
private fun createRetrofit(): Retrofit {
|
||||
val okHttpClient = createOkHttpClientWithDynamicCert(loadCertificateFromString(cert))
|
||||
val okHttpClient = createOkHttp()
|
||||
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(base)
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
package com.acitelight.aether.service
|
||||
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.acitelight.aether.model.ChallengeResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Call
|
||||
import okhttp3.EventListener
|
||||
import okhttp3.Handshake
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
|
||||
import org.bouncycastle.crypto.signers.Ed25519Signer
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Proxy
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.PrivateKey
|
||||
import java.security.Signature
|
||||
|
||||
@@ -17,13 +28,13 @@ object AuthManager {
|
||||
challengeBase64 = api!!.getChallenge(username).string()
|
||||
}catch (e: Exception)
|
||||
{
|
||||
print(e.message)
|
||||
return null
|
||||
}
|
||||
|
||||
val signedBase64 = signChallenge(db64(privateKey), db64(challengeBase64))
|
||||
|
||||
return try {
|
||||
api!!.verifyChallenge(username, ChallengeResponse(response = signedBase64)).string()
|
||||
api.verifyChallenge(username, ChallengeResponse(response = signedBase64)).string()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.acitelight.aether.service
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
@Singleton
|
||||
class SettingsDataStoreManager @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
companion object {
|
||||
val USER_NAME_KEY = stringPreferencesKey("user_name")
|
||||
val PRIVATE_KEY = stringPreferencesKey("private_key")
|
||||
val URL_KEY = stringPreferencesKey("url")
|
||||
val CERT_KEY = stringPreferencesKey("cert")
|
||||
val USE_SELF_SIGNED_KEY = booleanPreferencesKey("use_self_signed")
|
||||
}
|
||||
|
||||
val userNameFlow: Flow<String> = context.dataStore.data.map { preferences ->
|
||||
preferences[USER_NAME_KEY] ?: ""
|
||||
}
|
||||
|
||||
val privateKeyFlow: Flow<String> = context.dataStore.data.map { preferences ->
|
||||
preferences[PRIVATE_KEY] ?: ""
|
||||
}
|
||||
|
||||
val urlFlow: Flow<String> = context.dataStore.data.map { preferences ->
|
||||
preferences[URL_KEY] ?: ""
|
||||
}
|
||||
|
||||
val certFlow: Flow<String> = context.dataStore.data.map { preferences ->
|
||||
preferences[CERT_KEY] ?: ""
|
||||
}
|
||||
|
||||
val useSelfSignedFlow: Flow<Boolean> = context.dataStore.data.map { preferences ->
|
||||
preferences[USE_SELF_SIGNED_KEY] ?: false
|
||||
}
|
||||
|
||||
suspend fun saveUserName(name: String) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[USER_NAME_KEY] = name
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun savePrivateKey(key: String) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PRIVATE_KEY] = key
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveUrl(url: String) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[URL_KEY] = url
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveCert(cert: String) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[CERT_KEY] = cert
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveUseSelfSigned(useSelfSigned: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[USE_SELF_SIGNED_KEY] = useSelfSigned
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearAll() {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user