From b74ca98bf9622fbdd19526638fc1f5c3bf936f05 Mon Sep 17 00:00:00 2001 From: acite <1498045907@qq.com> Date: Tue, 26 Aug 2025 02:10:35 +0800 Subject: [PATCH] [feat] Server Configure& Https --- .../acitelight/aether/service/ApiClient.kt | 92 ++++++++- .../acitelight/aether/service/AuthManager.kt | 6 +- .../acitelight/aether/service/MediaManager.kt | 12 +- .../com/acitelight/aether/view/MeScreen.kt | 186 ++++++++++++------ .../aether/viewModel/HomeScreenViewModel.kt | 18 +- .../aether/viewModel/MeScreenViewModel.kt | 52 ++++- 6 files changed, 289 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt index 7bc5e02..1902525 100644 --- a/app/src/main/java/com/acitelight/aether/service/ApiClient.kt +++ b/app/src/main/java/com/acitelight/aether/service/ApiClient.kt @@ -1,25 +1,101 @@ package com.acitelight.aether.service +import android.content.Context import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory 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.security.KeyStore +import java.security.cert.Certificate +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 object ApiClient { - const val base: String = "http://192.168.1.213/" + var base: String = "" + var domain: String = "" + var cert: String = "" private val json = Json { ignoreUnknownKeys = true } - private val retrofit = Retrofit.Builder() - .baseUrl(base) - .addConverterFactory(GsonConverterFactory.create()) - .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) - .build() + fun loadCertificateFromString(pemString: String): X509Certificate { + val certificateFactory = CertificateFactory.getInstance("X.509") + val decodedPem = pemString + .replace("-----BEGIN CERTIFICATE-----", "") + .replace("-----END CERTIFICATE-----", "") + .replace("\\s+".toRegex(), "") - val api: ApiInterface by lazy { - retrofit.create(ApiInterface::class.java) + val decodedBytes = android.util.Base64.decode(decodedPem, android.util.Base64.DEFAULT) + + ByteArrayInputStream(decodedBytes).use { inputStream -> + return certificateFactory.generateCertificate(inputStream) as X509Certificate + } + } + + fun createOkHttpClientWithDynamicCert(trustedCert: X509Certificate): OkHttpClient { + try { + val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply { + load(null, null) + setCertificateEntry("ca", trustedCert) + } + + val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm() + val tmf = TrustManagerFactory.getInstance(tmfAlgorithm).apply { + init(keyStore) + } + + val trustManager = tmf.trustManagers.first { it is X509TrustManager } as X509TrustManager + + val sslContext = SSLContext.getInstance("TLS").apply { + init(null, arrayOf(trustManager), null) + } + + return OkHttpClient.Builder() + .sslSocketFactory(sslContext.socketFactory, trustManager) + .build() + + } catch (e: Exception) { + throw RuntimeException("Failed to create OkHttpClient with dynamic certificate", e) + } + } + + private fun createRetrofit(): Retrofit { + val okHttpClient = createOkHttpClientWithDynamicCert(loadCertificateFromString(cert)) + + return Retrofit.Builder() + .baseUrl(base) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + } + + + var api: ApiInterface? = null + + fun apply(url: String, crt: String) + { + try{ + domain = url.toHttpUrlOrNull()?.host !! + cert = crt + base = url + api = createRetrofit().create(ApiInterface::class.java) + }catch (e: Exception) + { + api = null + base = "" + domain = "" + cert = "" + } } } \ No newline at end of file diff --git a/app/src/main/java/com/acitelight/aether/service/AuthManager.kt b/app/src/main/java/com/acitelight/aether/service/AuthManager.kt index 82efb0f..1a09122 100644 --- a/app/src/main/java/com/acitelight/aether/service/AuthManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/AuthManager.kt @@ -9,12 +9,12 @@ import java.security.PrivateKey import java.security.Signature object AuthManager { - suspend fun fetchToken(baseUrl: String, username: String, privateKey: String): String? { + suspend fun fetchToken(username: String, privateKey: String): String? { val api = ApiClient.api var challengeBase64 = "" try{ - challengeBase64 = api.getChallenge(username).string() + challengeBase64 = api!!.getChallenge(username).string() }catch (e: Exception) { print(e.message) @@ -23,7 +23,7 @@ object AuthManager { 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 diff --git a/app/src/main/java/com/acitelight/aether/service/MediaManager.kt b/app/src/main/java/com/acitelight/aether/service/MediaManager.kt index 2178a42..a5b6c3a 100644 --- a/app/src/main/java/com/acitelight/aether/service/MediaManager.kt +++ b/app/src/main/java/com/acitelight/aether/service/MediaManager.kt @@ -15,7 +15,7 @@ object MediaManager { try { - val j = ApiClient.api.getVideoClasses(token) + val j = ApiClient.api!!.getVideoClasses(token) return j.toList() }catch(e: Exception) { @@ -26,7 +26,7 @@ object MediaManager suspend fun listVideos(klass: String): List