Use CipherSource to decrypt responses by streaming (#10253)
This commit is contained in:
parent
e4cd4833e0
commit
f50bec002b
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'izneo (webtoons)'
|
extName = 'izneo (webtoons)'
|
||||||
extClass = '.IzneoFactory'
|
extClass = '.IzneoFactory'
|
||||||
extVersionCode = 7
|
extVersionCode = 8
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,14 @@ import android.util.Base64
|
|||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.asResponseBody
|
||||||
|
import okio.buffer
|
||||||
|
import okio.cipherSource
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
object ImageInterceptor : Interceptor {
|
object ImageInterceptor : Interceptor {
|
||||||
private val mediaType = "image/jpeg".toMediaType()
|
|
||||||
|
|
||||||
private inline val AES: Cipher
|
private inline val AES: Cipher
|
||||||
get() = Cipher.getInstance("AES/CBC/PKCS7Padding")
|
get() = Cipher.getInstance("AES/CBC/PKCS7Padding")
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ object ImageInterceptor : Interceptor {
|
|||||||
|
|
||||||
private fun Response.decode(key: ByteArray, iv: ByteArray) = AES.let {
|
private fun Response.decode(key: ByteArray, iv: ByteArray) = AES.let {
|
||||||
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
||||||
newBuilder().body(it.doFinal(body.bytes()).toResponseBody(mediaType)).build()
|
newBuilder().body(body.source().cipherSource(it).buffer().asResponseBody("image/jpeg".toMediaType())).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.atob() = Base64.decode(this, Base64.URL_SAFE)
|
private fun String.atob() = Base64.decode(this, Base64.URL_SAFE)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'COMIC FUZ'
|
extName = 'COMIC FUZ'
|
||||||
extClass = '.ComicFuz'
|
extClass = '.ComicFuz'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,14 @@ package eu.kanade.tachiyomi.extension.ja.comicfuz
|
|||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.asResponseBody
|
||||||
|
import okio.buffer
|
||||||
|
import okio.cipherSource
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
object ImageInterceptor : Interceptor {
|
object ImageInterceptor : Interceptor {
|
||||||
private val mediaType = "image/jpeg".toMediaType()
|
|
||||||
|
|
||||||
private inline val AES: Cipher
|
private inline val AES: Cipher
|
||||||
get() = Cipher.getInstance("AES/CBC/PKCS7Padding")
|
get() = Cipher.getInstance("AES/CBC/PKCS7Padding")
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ object ImageInterceptor : Interceptor {
|
|||||||
).build(),
|
).build(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val body = response.body.bytes()
|
val body = response
|
||||||
.decode(key.decodeHex(), iv.decodeHex())
|
.decode(key.decodeHex(), iv.decodeHex())
|
||||||
|
|
||||||
return response.newBuilder()
|
return response.newBuilder()
|
||||||
@ -37,9 +37,9 @@ object ImageInterceptor : Interceptor {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ByteArray.decode(key: ByteArray, iv: ByteArray) = AES.let {
|
private fun Response.decode(key: ByteArray, iv: ByteArray) = AES.let {
|
||||||
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
||||||
it.doFinal(this).toResponseBody(mediaType)
|
body.source().cipherSource(it).buffer().asResponseBody("image/jpeg".toMediaType())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.decodeHex(): ByteArray {
|
private fun String.decodeHex(): ByteArray {
|
||||||
|
@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.extension.pt.randomscan
|
|||||||
import eu.kanade.tachiyomi.lib.zipinterceptor.ZipInterceptor
|
import eu.kanade.tachiyomi.lib.zipinterceptor.ZipInterceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.io.ByteArrayInputStream
|
import okio.BufferedSource
|
||||||
|
import okio.buffer
|
||||||
|
import okio.cipherSource
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@ -13,18 +15,18 @@ import javax.crypto.spec.IvParameterSpec
|
|||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
class LuraZipInterceptor : ZipInterceptor() {
|
class LuraZipInterceptor : ZipInterceptor() {
|
||||||
fun decryptFile(encryptedData: ByteArray, keyBytes: ByteArray): ByteArray {
|
private fun decryptFile(encryptedData: BufferedSource, keyBytes: ByteArray): BufferedSource {
|
||||||
val keyHash = MessageDigest.getInstance("SHA-256").digest(keyBytes)
|
val keyHash = MessageDigest.getInstance("SHA-256").digest(keyBytes)
|
||||||
|
|
||||||
val key: SecretKey = SecretKeySpec(keyHash, "AES")
|
val key: SecretKey = SecretKeySpec(keyHash, "AES")
|
||||||
|
|
||||||
val counter = encryptedData.copyOfRange(0, 8)
|
val counter = encryptedData.readByteArray(8)
|
||||||
val iv = IvParameterSpec(counter)
|
val iv = IvParameterSpec(counter)
|
||||||
|
|
||||||
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
|
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key, iv)
|
cipher.init(Cipher.DECRYPT_MODE, key, iv)
|
||||||
|
|
||||||
val decryptedData = cipher.doFinal(encryptedData.copyOfRange(8, encryptedData.size))
|
val decryptedData = encryptedData.cipherSource(cipher).buffer()
|
||||||
|
|
||||||
return decryptedData
|
return decryptedData
|
||||||
}
|
}
|
||||||
@ -35,11 +37,11 @@ class LuraZipInterceptor : ZipInterceptor() {
|
|||||||
|
|
||||||
override fun zipGetByteStream(request: Request, response: Response): InputStream {
|
override fun zipGetByteStream(request: Request, response: Response): InputStream {
|
||||||
val keyData = listOf("obra_id", "slug", "cap_id", "cap_slug").joinToString("") {
|
val keyData = listOf("obra_id", "slug", "cap_id", "cap_slug").joinToString("") {
|
||||||
request.url.queryParameterValues(it).first().toString()
|
request.url.queryParameter(it)!!
|
||||||
}.toByteArray(StandardCharsets.UTF_8)
|
}.toByteArray(StandardCharsets.UTF_8)
|
||||||
val encryptedData = response.body.bytes()
|
val encryptedData = response.body.source()
|
||||||
|
|
||||||
val decryptedData = decryptFile(encryptedData, keyData)
|
val decryptedData = decryptFile(encryptedData, keyData)
|
||||||
return ByteArrayInputStream(decryptedData)
|
return decryptedData.inputStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Manwa'
|
extName = 'Manwa'
|
||||||
extClass = '.Manwa'
|
extClass = '.Manwa'
|
||||||
extVersionCode = 12
|
extVersionCode = 13
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,9 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.asResponseBody
|
||||||
|
import okio.buffer
|
||||||
|
import okio.cipherSource
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -57,13 +59,12 @@ class Manwa : ParsedHttpSource(), ConfigurableSource {
|
|||||||
val originalResponse: Response = chain.proceed(chain.request())
|
val originalResponse: Response = chain.proceed(chain.request())
|
||||||
if (originalResponse.request.url.toString().endsWith("?v=20220724")) {
|
if (originalResponse.request.url.toString().endsWith("?v=20220724")) {
|
||||||
// Decrypt images in mangas
|
// Decrypt images in mangas
|
||||||
val orgBody = originalResponse.body.bytes()
|
|
||||||
val key = "my2ecret782ecret".toByteArray()
|
val key = "my2ecret782ecret".toByteArray()
|
||||||
val aesKey = SecretKeySpec(key, "AES")
|
val aesKey = SecretKeySpec(key, "AES")
|
||||||
val cipher = Cipher.getInstance("AES/CBC/NOPADDING")
|
val cipher = Cipher.getInstance("AES/CBC/NOPADDING")
|
||||||
cipher.init(Cipher.DECRYPT_MODE, aesKey, IvParameterSpec(key))
|
cipher.init(Cipher.DECRYPT_MODE, aesKey, IvParameterSpec(key))
|
||||||
val result = cipher.doFinal(orgBody)
|
val result = originalResponse.body.source().cipherSource(cipher).buffer()
|
||||||
val newBody = result.toResponseBody("image/webp".toMediaTypeOrNull())
|
val newBody = result.asResponseBody("image/webp".toMediaTypeOrNull())
|
||||||
originalResponse.newBuilder()
|
originalResponse.newBuilder()
|
||||||
.body(newBody)
|
.body(newBody)
|
||||||
.build()
|
.build()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'COLAMANGA'
|
extName = 'COLAMANGA'
|
||||||
extClass = '.Onemanhua'
|
extClass = '.Onemanhua'
|
||||||
extVersionCode = 25
|
extVersionCode = 26
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,14 @@ package eu.kanade.tachiyomi.extension.zh.onemanhua
|
|||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
import okhttp3.ResponseBody.Companion.asResponseBody
|
||||||
|
import okio.buffer
|
||||||
|
import okio.cipherSource
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
class ColaMangaImageInterceptor : Interceptor {
|
class ColaMangaImageInterceptor : Interceptor {
|
||||||
private val iv = "0000000000000000".toByteArray()
|
|
||||||
private val mediaType = "image/jpeg".toMediaType()
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val response = chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
@ -22,12 +21,13 @@ class ColaMangaImageInterceptor : Interceptor {
|
|||||||
|
|
||||||
val key = request.url.fragment!!.substringAfter(KEY_PREFIX).toByteArray()
|
val key = request.url.fragment!!.substringAfter(KEY_PREFIX).toByteArray()
|
||||||
val output = Cipher.getInstance("AES/CBC/PKCS7Padding").let {
|
val output = Cipher.getInstance("AES/CBC/PKCS7Padding").let {
|
||||||
|
val iv = "0000000000000000".toByteArray()
|
||||||
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
it.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
||||||
it.doFinal(response.body.bytes())
|
response.body.source().cipherSource(it).buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.newBuilder()
|
return response.newBuilder()
|
||||||
.body(output.toResponseBody(mediaType))
|
.body(output.asResponseBody("image/jpeg".toMediaType()))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user