Skip to content

Commit

Permalink
Fix Mp4Upload on recent Android versions
Browse files Browse the repository at this point in the history
  • Loading branch information
rubengees committed Nov 29, 2020
1 parent 5257e6d commit c85aec4
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 33 deletions.
11 changes: 10 additions & 1 deletion src/main/kotlin/me/proxer/app/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,16 @@ class MainApplication : Application() {
.build()

val vmPolicy = StrictModeCompat.VmPolicy.Builder()
.detectAll()
.detectActivityLeaks()
.detectCleartextNetwork()
.detectFileUriExposure()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.detectLeakedSqlLiteObjects()
.detectContentUriWithoutPermission()
.detectNonSdkApiUsage()
.detectImplicitDirectBoot()
.detectCredentialProtectedWhileLocked()
.penaltyLog()
.build()

Expand Down
35 changes: 3 additions & 32 deletions src/main/kotlin/me/proxer/app/MainModules.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package me.proxer.app

import android.content.res.Resources
import android.os.Build
import androidx.preference.PreferenceManager
import androidx.room.Room
import androidx.security.crypto.EncryptedSharedPreferences
Expand Down Expand Up @@ -55,6 +54,7 @@ import me.proxer.app.profile.settings.ProfileSettingsViewModel
import me.proxer.app.profile.topten.TopTenViewModel
import me.proxer.app.settings.status.ServerStatusViewModel
import me.proxer.app.ui.LinkCheckViewModel
import me.proxer.app.util.Mp4UploadTrustManagerWorkaround
import me.proxer.app.util.Validators
import me.proxer.app.util.data.HawkMoshiParser
import me.proxer.app.util.data.InstantJsonAdapter
Expand Down Expand Up @@ -87,7 +87,6 @@ import org.koin.dsl.module
import org.threeten.bp.Instant
import java.io.File
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
Expand Down Expand Up @@ -162,38 +161,10 @@ private val applicationModules = module {
else -> null
}

val trustManager = object : X509TrustManager {
private val platformTrustManager = Platform.get().platformTrustManager()

override fun getAcceptedIssuers(): Array<X509Certificate> = platformTrustManager.acceptedIssuers

override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {
platformTrustManager.checkClientTrusted(certs, authType)
}

override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {
val mp4UploadCert = certs.find { it.subjectDN.name == "CN=*.mp4upload.com" }

if (mp4UploadCert != null) {
// The certificate chain of MP4Upload contains an expired cross-signed certificate which incorrectly
// leads to an error on older Android Versions.
// Avoid checking the entire chain here to workaround that.
//
// THIS IS A SECURITY ISSUE AND NEEDS TO BE REMOVED AS SOON AS POSSIBLE.
mp4UploadCert.checkValidity()
} else {
platformTrustManager.checkServerTrusted(certs, authType)
}
}
}
val trustManager = Mp4UploadTrustManagerWorkaround.create()

OkHttpClient.Builder()
.apply {
// Tested: Only necessary on Lollipop.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
sslSocketFactory(Platform.get().newSslSocketFactory(trustManager), trustManager)
}
}
.sslSocketFactory(Platform.get().newSslSocketFactory(trustManager), trustManager)
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT))
.socketFactory(TaggedSocketFactory())
.connectTimeout(5, TimeUnit.SECONDS)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package me.proxer.app.util

import android.os.Build
import androidx.annotation.RequiresApi
import okhttp3.internal.platform.Platform
import java.net.Socket
import java.security.cert.X509Certificate
import javax.net.ssl.SSLEngine
import javax.net.ssl.X509ExtendedTrustManager
import javax.net.ssl.X509TrustManager

object Mp4UploadTrustManagerWorkaround {

fun create(): X509TrustManager {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
createSdk24()
} else {
createSdk21()
}
}

private fun createSdk21(): X509TrustManager {
return object : X509TrustManager {
private val platformTrustManager = Platform.get().platformTrustManager()

override fun getAcceptedIssuers(): Array<X509Certificate> = platformTrustManager.acceptedIssuers

override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {
platformTrustManager.checkClientTrusted(chain, authType)
}

override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {
checkServerTrustedWithMp4Upload(chain) {
platformTrustManager.checkServerTrusted(chain, authType)
}
}
}
}

@RequiresApi(Build.VERSION_CODES.N)
private fun createSdk24(): X509ExtendedTrustManager {
return object : X509ExtendedTrustManager() {
private val platformTrustManager = Platform.get().platformTrustManager() as X509ExtendedTrustManager

override fun getAcceptedIssuers(): Array<X509Certificate> = platformTrustManager.acceptedIssuers

override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String, socket: Socket) {
platformTrustManager.checkClientTrusted(chain, authType, socket)
}

override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String, engine: SSLEngine) {
platformTrustManager.checkClientTrusted(chain, authType, engine)
}

override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {
platformTrustManager.checkClientTrusted(chain, authType)
}

override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String, socket: Socket) {
checkServerTrustedWithMp4Upload(chain) {
platformTrustManager.checkServerTrusted(chain, authType, socket)
}
}

override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String, engine: SSLEngine) {
checkServerTrustedWithMp4Upload(chain) {
platformTrustManager.checkServerTrusted(chain, authType, engine)
}
}

override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {
checkServerTrustedWithMp4Upload(chain) {
platformTrustManager.checkServerTrusted(chain, authType)
}
}
}
}

private fun checkServerTrustedWithMp4Upload(chain: Array<out X509Certificate>, delegate: () -> Unit) {
val mp4UploadCert = chain.find { it.subjectDN.name == "CN=*.mp4upload.com" }

if (mp4UploadCert != null) {
// The certificate chain of MP4Upload contains an expired cross-signed certificate which incorrectly
// leads to an error on various Android Versions.
// Avoid checking the entire chain here to workaround that.
//
// THIS IS A SECURITY ISSUE AND NEEDS TO BE REMOVED AS SOON AS POSSIBLE.
mp4UploadCert.checkValidity()
} else {
delegate()
}
}
}

0 comments on commit c85aec4

Please sign in to comment.