trust all certs

This commit is contained in:
Li ZongYing 2024-01-11 19:33:31 +08:00
parent 7821e8fa58
commit b075a7db81
4 changed files with 145 additions and 89 deletions

View File

@ -5,6 +5,7 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.Signature import android.content.pm.Signature
import android.content.pm.SigningInfo import android.content.pm.SigningInfo
import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -36,7 +37,7 @@ class MainActivity : FragmentActivity() {
private lateinit var gestureDetector: GestureDetector private lateinit var gestureDetector: GestureDetector
private val handler = Handler() private val handler = Handler()
private val delay: Long = 3000 private val delay: Long = 4000
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -170,6 +171,40 @@ class MainActivity : FragmentActivity() {
} }
} }
private fun showHelp() {
val versionName = getPackageInfo().versionName
val textView = TextView(this)
textView.text =
"当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
textView.setBackgroundColor(0xFF263238.toInt())
textView.setPadding(20, 50, 20, 20)
val imageView = ImageView(this)
val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate)
imageView.setImageDrawable(drawable)
imageView.setBackgroundColor(Color.WHITE)
val linearLayout = LinearLayout(this)
linearLayout.orientation = LinearLayout.VERTICAL
linearLayout.addView(textView)
linearLayout.addView(imageView)
val layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
imageView.layoutParams = layoutParams
textView.layoutParams = layoutParams
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder
.setView(linearLayout)
val dialog: AlertDialog = builder.create()
dialog.show()
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_BACK -> { KeyEvent.KEYCODE_BACK -> {
@ -192,35 +227,13 @@ class MainActivity : FragmentActivity() {
return true return true
} }
KeyEvent.KEYCODE_SETTINGS -> {
showHelp()
return true
}
KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_MENU -> {
val versionName = getPackageInfo().versionName showHelp()
val textView = TextView(this)
textView.text =
"当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
val imageView = ImageView(this)
val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate)
imageView.setImageDrawable(drawable)
val linearLayout = LinearLayout(this)
linearLayout.orientation = LinearLayout.VERTICAL
linearLayout.addView(textView)
linearLayout.addView(imageView)
val layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
imageView.layoutParams = layoutParams
textView.layoutParams = layoutParams
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder
.setView(linearLayout)
val dialog: AlertDialog = builder.create()
dialog.show()
return true return true
} }

View File

@ -87,7 +87,10 @@ class Request {
ysp = YSP(context) ysp = YSP(context)
} }
var call: Call<LiveInfo>? = null
fun fetchVideo(tvModel: TVViewModel, cookie: String) { fun fetchVideo(tvModel: TVViewModel, cookie: String) {
call?.cancel()
if (::myRunnable.isInitialized) { if (::myRunnable.isInitialized) {
handler.removeCallbacks(myRunnable) handler.removeCallbacks(myRunnable)
} }
@ -97,71 +100,71 @@ class Request {
tvModel.seq = 0 tvModel.seq = 0
val data = ysp?.switch(tvModel) val data = ysp?.switch(tvModel)
val request = data?.let { LiveInfoRequest(it) } val request = data?.let { LiveInfoRequest(it) }
call = request?.let { yspApiService.getLiveInfo("guid=${ysp?.getGuid()}; $cookie", it) }
request?.let { yspApiService.getLiveInfo(cookie, it) } call?.enqueue(object : Callback<LiveInfo> {
?.enqueue(object : Callback<LiveInfo> { override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) {
override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) { if (response.isSuccessful) {
if (response.isSuccessful) { val liveInfo = response.body()
val liveInfo = response.body() if (liveInfo?.data?.playurl != null) {
if (liveInfo?.data?.playurl != null) { val chanll = liveInfo.data.chanll
val chanll = liveInfo.data.chanll val decodedBytes = Base64.decode(
val decodedBytes = Base64.decode( chanll.substring(9, chanll.length - 3),
chanll.substring(9, chanll.length - 3), Base64.DEFAULT
Base64.DEFAULT )
) val decodedString = String(decodedBytes)
val decodedString = String(decodedBytes) val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""") val matchResult = regex.find(decodedString)
val matchResult = regex.find(decodedString) if (matchResult != null) {
if (matchResult != null) { val (key, iv) = matchResult.destructured
val (key, iv) = matchResult.destructured val keyBytes = Base64.decode(key, Base64.DEFAULT)
val keyBytes = Base64.decode(key, Base64.DEFAULT) val ivBytes = Base64.decode(iv, Base64.DEFAULT)
val ivBytes = Base64.decode(iv, Base64.DEFAULT) val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES( keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0), ivBytes
ivBytes ).uppercase()
).uppercase() Log.i(TAG, "$title url $url")
Log.i(TAG, "$title url $url") tvModel.addVideoUrl(url)
tvModel.addVideoUrl(url) tvModel.allReady()
tvModel.allReady() tvModel.retryTimes = 0
tvModel.retryTimes = 0 myRunnable = MyRunnable(tvModel)
myRunnable = MyRunnable(tvModel) handler.post(myRunnable)
handler.post(myRunnable)
} else {
Log.e(TAG, "$title key error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchVideo(tvModel, cookie)
}
}
} else { } else {
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") { Log.e(TAG, "$title key error")
Log.e(TAG, "$title error ${liveInfo.data.errinfo}") if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.setErrInfo(liveInfo.data.errinfo) tvModel.retryTimes++
} else { fetchVideo(tvModel, cookie)
Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchVideo(tvModel, cookie)
}
} }
} }
} else { } else {
Log.e(TAG, "$title status error") if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { Log.e(TAG, "$title error ${liveInfo.data.errinfo}")
tvModel.retryTimes++ tvModel.setErrInfo(liveInfo.data.errinfo)
fetchVideo(tvModel, cookie) } else {
Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchVideo(tvModel, cookie)
}
} }
} }
} } else {
Log.e(TAG, "$title status error")
override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
Log.e(TAG, "$title request error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
} }
}) }
override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
Log.e(TAG, "$title request error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++
fetchVideo(tvModel, cookie)
}
}
})
} }
fun fetchVideo(tvModel: TVViewModel) { fun fetchVideo(tvModel: TVViewModel) {
@ -172,7 +175,7 @@ class Request {
val token = response.body()?.data?.token val token = response.body()?.data?.token
Log.i(TAG, "info success $token") Log.i(TAG, "info success $token")
val cookie = val cookie =
"guid=1; vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" "vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else { } else {
Log.e(TAG, "info status error") Log.e(TAG, "info status error")
@ -197,7 +200,7 @@ class Request {
if (tvModel.needToken) { if (tvModel.needToken) {
fetchVideo(tvModel) fetchVideo(tvModel)
} else { } else {
val cookie = "guid=1; vplatform=109" val cookie = "vplatform=109"
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
} }

View File

@ -5,6 +5,9 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.protobuf.ProtoConverterFactory import retrofit2.converter.protobuf.ProtoConverterFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
class ApiClient { class ApiClient {
@ -13,9 +16,7 @@ class ApiClient {
private val protoUrl = "https://capi.yangshipin.cn/" private val protoUrl = "https://capi.yangshipin.cn/"
private val traceUrl = "https://btrace.yangshipin.cn/" private val traceUrl = "https://btrace.yangshipin.cn/"
private var okHttpClient = OkHttpClient.Builder() private var okHttpClient = getUnsafeOkHttpClient()
.dns(DnsCache())
.build()
val yspApiService: YSPApiService by lazy { val yspApiService: YSPApiService by lazy {
Retrofit.Builder() Retrofit.Builder()
@ -48,4 +49,40 @@ class ApiClient {
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.build().create(YSPBtraceService::class.java) .build().create(YSPBtraceService::class.java)
} }
private fun getUnsafeOkHttpClient(): OkHttpClient {
try {
val trustAllCerts: Array<TrustManager> = arrayOf(
object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}
override fun checkServerTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return emptyArray()
}
}
)
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
.dns(DnsCache())
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
} }

View File

@ -71,7 +71,10 @@ class YSP(var context: Context) {
randStr = getRand() randStr = getRand()
guid = newGuid() if (tvModel.retryTimes > 0) {
guid = newGuid()
}
timeStr = getTimeStr() timeStr = getTimeStr()
// guid = "lq3oqitm_1e15dnzgjnb" // guid = "lq3oqitm_1e15dnzgjnb"
@ -107,7 +110,7 @@ class YSP(var context: Context) {
return guid return guid
} }
fun newGuid(): String { private fun newGuid(): String {
guid = generateGuid() guid = generateGuid()
with(sharedPref!!.edit()) { with(sharedPref!!.edit()) {
putString("guid", guid) putString("guid", guid)