Merge pull request #2436 from Xwite/master

移除和hutool重复的实现
pull/2439/head
Xwite 2 years ago committed by GitHub
commit 9a53f0e5e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      app/src/main/assets/help/jsHelp.md
  2. 1
      app/src/main/assets/updateLog.md
  3. 2
      app/src/main/java/io/legado/app/constant/AppPattern.kt
  4. 9
      app/src/main/java/io/legado/app/data/entities/BaseSource.kt
  5. 392
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  6. 3
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  7. 304
      app/src/main/java/io/legado/app/utils/EncoderUtils.kt
  8. 26
      app/src/main/java/io/legado/app/utils/NetworkUtils.kt
  9. 7
      app/src/main/java/io/legado/app/utils/StringExtensions.kt
  10. 36
      app/src/main/java/io/legado/app/utils/StringUtils.kt
  11. 4
      app/src/main/java/io/legado/app/utils/SvgUtils.kt

@ -157,34 +157,20 @@ deleteFile(path: String)
> 其他加密方式 可在js中[调用](https://m.jb51.net/article/92138.htm)[hutool-crypto](https://www.hutool.cn/docs/#/) > 其他加密方式 可在js中[调用](https://m.jb51.net/article/92138.htm)[hutool-crypto](https://www.hutool.cn/docs/#/)
* AES * 对称加密AES/DES/TripleDES
> transformation默认实现AES/ECB/PKCS5Padding > AES transformation默认实现AES/ECB/PKCS5Padding
``` > DES transformation默认实现DES/ECB/PKCS5Padding
java.aesDecodeToString(str: String, key: String, transformation: String, iv: String) > TripleDES tansformation默认实现DESede/ECB/PKCS5Padding
> 内部实现为cn.hutool.crypto 解密加密接口支持ByteArray|Base64String|HexString|InputStream
java.aesBase64DecodeToString(str: String, key: String, transformation: String, iv: String) > 输入参数key iv 支持ByteArray|Base64String|HexString|Utf8String
```
java.aesEncodeToString(str: String, key: String, transformation: String, iv: String) //解密为ByteArray 字符串
java.createSymmetricCrypto(transformation, key, iv).decrypt(data)
java.aesEncodeToBase64String(str: String, key: String, transformation: String, iv: String) java.createSymmetricCrypto(transformation, key, iv).decryptStr(data)
``` //加密为ByteArray Base64字符 HEX字符
* DES java.createSymmetricCrypto(transformation, key, iv).encrypt(data)
> transformation默认实现DES/ECB/PKCS5Padding java.createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
``` java.createSymmetricCrypto(transformation, key, iv).encryptHex(data)
java.desDecodeToString(str: String, key: String, transformation: String, iv: String)
java.desBase64DecodeToString(str: String, key: String, transformation: String, iv: String)
java.desEncodeToString(str: String, key: String, transformation: String, iv: String)
java.desEncodeToBase64String(str: String, key: String, transformation: String, iv: String)
```
* 3DES
> tansformation默认实现DESede/ECB/PKCS5Padding
```
java.tripleDESEncodeBase64Str(data: String,key: String,mode: String,padding: String,iv: String): String?
java.tripleDESDecodeStr(data: String,key: String,mode: String,padding: String,iv: String): String?
``` ```
* 摘要 * 摘要
> MD5 SHA-1 SHA-224 SHA-256 SHA-384 SHA-512 > MD5 SHA-1 SHA-224 SHA-256 SHA-384 SHA-512

@ -17,6 +17,7 @@
* web服务和朗读服务添加唤醒锁 * web服务和朗读服务添加唤醒锁
* 修复搜索禁用书源的bug * 修复搜索禁用书源的bug
* 修复本地书籍很慢的问题 * 修复本地书籍很慢的问题
* 更改内置对称加密函数,请看文档
**2022/10/14** **2022/10/14**

@ -13,6 +13,8 @@ object AppPattern {
//dataURL图片类型 //dataURL图片类型
val dataUriRegex = Regex("data:.*?;base64,(.*)") val dataUriRegex = Regex("data:.*?;base64,(.*)")
//Base64字符串
val base64Regex = Regex("^[a-zA-Z0-9\\+\\/=]+$")
val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著")
val authorRegex = Regex("^\\s*作\\s*者[::\\s]+|\\s+著") val authorRegex = Regex("^\\s*作\\s*者[::\\s]+|\\s+著")

@ -2,6 +2,7 @@ package io.legado.app.data.entities
import android.util.Base64 import android.util.Base64
import com.script.SimpleBindings import com.script.SimpleBindings
import cn.hutool.crypto.symmetric.AES
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.data.entities.rule.RowUi import io.legado.app.data.entities.rule.RowUi
@ -119,10 +120,7 @@ interface BaseSource : JsExtensions {
try { try {
val key = AppConst.androidId.encodeToByteArray(0, 16) val key = AppConst.androidId.encodeToByteArray(0, 16)
val cache = CacheManager.get("userInfo_${getKey()}") ?: return null val cache = CacheManager.get("userInfo_${getKey()}") ?: return null
val encodeBytes = Base64.decode(cache, Base64.DEFAULT) return AES(key).decryptStr(cache)
val decodeBytes = EncoderUtils.decryptAES(encodeBytes, key)
?: return null
return String(decodeBytes)
} catch (e: Exception) { } catch (e: Exception) {
AppLog.put("获取登陆信息出错", e) AppLog.put("获取登陆信息出错", e)
return null return null
@ -139,8 +137,7 @@ interface BaseSource : JsExtensions {
fun putLoginInfo(info: String): Boolean { fun putLoginInfo(info: String): Boolean {
return try { return try {
val key = (AppConst.androidId).encodeToByteArray(0, 16) val key = (AppConst.androidId).encodeToByteArray(0, 16)
val encodeBytes = EncoderUtils.encryptAES(info.toByteArray(), key) val encodeStr = AES(key).encryptBase64(info)
val encodeStr = Base64.encodeToString(encodeBytes, Base64.DEFAULT)
CacheManager.put("userInfo_${getKey()}", encodeStr) CacheManager.put("userInfo_${getKey()}", encodeStr)
true true
} catch (e: Exception) { } catch (e: Exception) {

@ -5,6 +5,8 @@ import android.util.Base64
import androidx.annotation.Keep import androidx.annotation.Keep
import cn.hutool.crypto.digest.DigestUtil import cn.hutool.crypto.digest.DigestUtil
import cn.hutool.crypto.digest.HMac import cn.hutool.crypto.digest.HMac
import cn.hutool.core.util.HexUtil
import cn.hutool.crypto.symmetric.SymmetricCrypto
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.dateFormat import io.legado.app.constant.AppConst.dateFormat
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
@ -174,18 +176,15 @@ interface JsExtensions {
/** /**
* 缓存以文本方式保存的文件 .js .txt等 * 缓存以文本方式保存的文件 .js .txt等
* @param urlStr 网络文件的链接
* @param saveTime 缓存时间单位
* @return 返回缓存后的文件内容
*/ */
fun cacheFile(urlStr: String): String? { fun cacheFile(urlStr: String): String? {
return cacheFile(urlStr, 0) return cacheFile(urlStr, 0)
} }
/** fun cacheFile(urlStr: String, saveTime: Int): String? {
* 缓存以文本方式保存的文件 .js .txt等
* @param urlStr 网络文件的链接
* @param saveTime 缓存时间单位
* @return 返回缓存后的文件内容
*/
fun cacheFile(urlStr: String, saveTime: Int = 0): String? {
val key = md5Encode16(urlStr) val key = md5Encode16(urlStr)
val cache = CacheManager.getFile(key) val cache = CacheManager.getFile(key)
if (cache.isNullOrBlank()) { if (cache.isNullOrBlank()) {
@ -200,7 +199,11 @@ interface JsExtensions {
/** /**
*js实现读取cookie *js实现读取cookie
*/ */
fun getCookie(tag: String, key: String? = null): String { fun getCookie(tag: String): String {
return getCookie(tag, null)
}
fun getCookie(tag: String, key: String?): String {
return if (key != null) { return if (key != null) {
CookieStore.getKey(tag, key) CookieStore.getKey(tag, key)
} else { } else {
@ -245,7 +248,7 @@ interface JsExtensions {
) )
FileUtils.delete(path) FileUtils.delete(path)
val file = FileUtils.createFileIfNotExist(path) val file = FileUtils.createFileIfNotExist(path)
StringUtils.hexStringToByte(content).let { HexUtil.decodeHex(content).let {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
file.writeBytes(it) file.writeBytes(it)
} }
@ -510,7 +513,7 @@ interface JsExtensions {
val bytes = if (url.isAbsUrl()) { val bytes = if (url.isAbsUrl()) {
AnalyzeUrl(url, source = getSource()).getByteArray() AnalyzeUrl(url, source = getSource()).getByteArray()
} else { } else {
StringUtils.hexStringToByte(url) HexUtil.decodeHex(url)
} }
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
val zis = ZipInputStream(ByteArrayInputStream(bytes)) val zis = ZipInputStream(ByteArrayInputStream(bytes))
@ -647,354 +650,57 @@ interface JsExtensions {
//******************对称加密解密************************// //******************对称加密解密************************//
/////AES
/** /**
* AES 解码为 ByteArray * 在js中这样使用
* @param str 传入的AES加密的数据 * java.createSymmetricCrypto(transformation, key, iv).decrypt(data)
* @param key AES 解密的key * java.createSymmetricCrypto(transformation, key, iv).decryptStr(data)
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesDecodeToByteArray(
str: String, key: String, transformation: String, iv: String
): ByteArray? {
return try {
EncoderUtils.decryptAES(
data = str.encodeToByteArray(),
key = key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)
} catch (e: Exception) {
e.printOnDebug()
log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
null
}
}
/** * java.createSymmetricCrypto(transformation, key, iv).encrypt(data)
* AES 解码为 String * java.createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
* @param str 传入的AES加密的数据 * java.createSymmetricCrypto(transformation, key, iv).encryptHex(data)
* @param key AES 解密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/ */
fun aesDecodeToString( /* 自动转换key iv 到ByteArray 支持base64 hex uft8*/
str: String, key: String, transformation: String, iv: String fun createSymmetricCrypto(
): String? { transformation: String,
return aesDecodeToByteArray(str, key, transformation, iv)?.let { String(it) } key: String
): SymmetricCrypto {
return createSymmetricCrypto(transformation, key, null)
} }
/** fun createSymmetricCrypto(
* AES解码为String算法参数经过Base64加密 transformation: String,
*
* @param data 加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 解密后的字符串
*/
fun aesDecodeArgsBase64Str(
data: String,
key: String, key: String,
mode: String, iv: String?
padding: String, ): SymmetricCrypto {
iv: String return createSymmetricCrypto(
): String? {
return EncoderUtils.decryptAES(
data.encodeToByteArray(),
Base64.decode(key, Base64.NO_WRAP),
"AES/${mode}/${padding}",
Base64.decode(iv, Base64.NO_WRAP)
)?.let { String(it) }
}
/**
* 已经base64的AES 解码为 ByteArray
* @param str 传入的AES Base64加密的数据
* @param key AES 解密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesBase64DecodeToByteArray(
str: String, key: String, transformation: String, iv: String
): ByteArray? {
return try {
EncoderUtils.decryptBase64AES(
str.encodeToByteArray(),
key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)
} catch (e: Exception) {
e.printOnDebug()
log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
null
}
}
/**
* 已经base64的AES 解码为 String
* @param str 传入的AES Base64加密的数据
* @param key AES 解密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesBase64DecodeToString(
str: String, key: String, transformation: String, iv: String
): String? {
return aesBase64DecodeToByteArray(str, key, transformation, iv)?.let { String(it) }
}
/**
* 加密aes为ByteArray
* @param data 传入的原始数据
* @param key AES加密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesEncodeToByteArray(
data: String, key: String, transformation: String, iv: String
): ByteArray? {
return try {
EncoderUtils.encryptAES(
data.encodeToByteArray(),
key = key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)
} catch (e: Exception) {
e.printOnDebug()
log(e.localizedMessage ?: "aesEncodeToByteArrayERROR")
null
}
}
/**
* 加密aes为String
* @param data 传入的原始数据
* @param key AES加密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesEncodeToString(
data: String, key: String, transformation: String, iv: String
): String? {
return aesEncodeToByteArray(data, key, transformation, iv)?.let { String(it) }
}
/**
* 加密aes后Base64化的ByteArray
* @param data 传入的原始数据
* @param key AES加密的key
* @param transformation AES加密的方式
* @param iv ECB模式的偏移向量
*/
fun aesEncodeToBase64ByteArray(
data: String, key: String, transformation: String, iv: String
): ByteArray? {
return try {
EncoderUtils.encryptAES2Base64(
data.encodeToByteArray(),
key.encodeToByteArray(),
transformation, transformation,
iv.encodeToByteArray() StringUtils.encodeStringToByteArray(key),
StringUtils.encodeStringToByteArray(iv)
) )
} catch (e: Exception) {
e.printOnDebug()
log(e.localizedMessage ?: "aesEncodeToBase64ByteArrayERROR")
null
}
} }
/** /* 调用SymmetricCrypto key为null时使用随机密钥*/
* 加密aes后Base64化的String fun createSymmetricCrypto(
* @param data 传入的原始数据 transformation: String
* @param key AES加密的key ): SymmetricCrypto {
* @param transformation AES加密的方式 return createSymmetricCrypto(transformation, null, null)
* @param iv ECB模式的偏移向量
*/
fun aesEncodeToBase64String(
data: String, key: String, transformation: String, iv: String
): String? {
return aesEncodeToBase64ByteArray(data, key, transformation, iv)?.let { String(it) }
} }
fun createSymmetricCrypto(
/** transformation: String,
* AES加密并转为Base64算法参数经过Base64加密 key: ByteArray
* ): SymmetricCrypto {
* @param data 被加密的字符串 return createSymmetricCrypto(transformation, key, null)
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 加密后的Base64
*/
fun aesEncodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return EncoderUtils.encryptAES2Base64(
data.encodeToByteArray(),
Base64.decode(key, Base64.NO_WRAP),
"AES/${mode}/${padding}",
Base64.decode(iv, Base64.NO_WRAP)
)?.let { String(it) }
}
/////DES
fun desDecodeToString(
data: String, key: String, transformation: String, iv: String
): String? {
return EncoderUtils.decryptDES(
data.encodeToByteArray(),
key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)?.let { String(it) }
}
fun desBase64DecodeToString(
data: String, key: String, transformation: String, iv: String
): String? {
return EncoderUtils.decryptBase64DES(
data.encodeToByteArray(),
key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)?.let { String(it) }
}
fun desEncodeToString(
data: String, key: String, transformation: String, iv: String
): String? {
return EncoderUtils.encryptDES(
data.encodeToByteArray(),
key.encodeToByteArray(),
transformation,
iv.encodeToByteArray()
)?.let { String(it) }
} }
fun desEncodeToBase64String( fun createSymmetricCrypto(
data: String, key: String, transformation: String, iv: String transformation: String,
): String? { key: ByteArray?,
return EncoderUtils.encryptDES2Base64( iv: ByteArray?
data.encodeToByteArray(), ): SymmetricCrypto {
key.encodeToByteArray(), val symmetricCrypto = SymmetricCrypto(transformation, key)
transformation, return if (iv != null && !iv.isEmpty()) symmetricCrypto.setIv(iv) else symmetricCrypto
iv.encodeToByteArray()
)?.let { String(it) }
}
//////3DES
/**
* 3DES解密
*
* @param data 加密的字符串
* @param key 密钥
* @param mode 模式
* @param padding 补码方式
* @param iv 加盐
* @return 解密后的字符串
*/
fun tripleDESDecodeStr(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return EncoderUtils.decryptDESede(
data.encodeToByteArray(),
key.encodeToByteArray(),
"DESede/${mode}/${padding}",
iv.encodeToByteArray()
)?.let { String(it) }
}
/**
* 3DES解密算法参数经过Base64加密
*
* @param data 加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 解密后的字符串
*/
fun tripleDESDecodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return EncoderUtils.decryptDESede(
data.encodeToByteArray(),
Base64.decode(key, Base64.NO_WRAP),
"DESede/${mode}/${padding}",
Base64.decode(iv, Base64.NO_WRAP)
)?.let { String(it) }
}
/**
* 3DES加密并转为Base64
*
* @param data 被加密的字符串
* @param key 密钥
* @param mode 模式
* @param padding 补码方式
* @param iv 加盐
* @return 加密后的Base64
*/
fun tripleDESEncodeBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return EncoderUtils.encryptDESede2Base64(
data.encodeToByteArray(),
key.encodeToByteArray(),
"DESede/${mode}/${padding}",
iv.encodeToByteArray()
)?.let { String(it) }
}
/**
* 3DES加密并转为Base64算法参数经过Base64加密
*
* @param data 被加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 加密后的Base64
*/
fun tripleDESEncodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return EncoderUtils.encryptDESede2Base64(
data.encodeToByteArray(),
Base64.decode(key, Base64.NO_WRAP),
"DESede/${mode}/${padding}",
Base64.decode(iv, Base64.NO_WRAP)
)?.let { String(it) }
} }
//******************消息摘要/散列消息鉴别码************************// //******************消息摘要/散列消息鉴别码************************//

@ -5,6 +5,7 @@ import android.util.Base64
import androidx.annotation.Keep import androidx.annotation.Keep
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.script.SimpleBindings import com.script.SimpleBindings
import cn.hutool.core.util.HexUtil
import io.legado.app.constant.AppConst.SCRIPT_ENGINE import io.legado.app.constant.AppConst.SCRIPT_ENGINE
import io.legado.app.constant.AppConst.UA_NAME import io.legado.app.constant.AppConst.UA_NAME
import io.legado.app.constant.AppPattern.JS_PATTERN import io.legado.app.constant.AppPattern.JS_PATTERN
@ -350,7 +351,7 @@ class AnalyzeUrl(
useWebView: Boolean = true, useWebView: Boolean = true,
): StrResponse { ): StrResponse {
if (type != null) { if (type != null) {
return StrResponse(url, StringUtils.byteToHexString(getByteArrayAwait())) return StrResponse(url, HexUtil.encodeHexStr(getByteArrayAwait()))
} }
val concurrentRecord = fetchStart() val concurrentRecord = fetchStart()
setCookie(source?.getKey()) setCookie(source?.getKey())

@ -1,13 +1,9 @@
package io.legado.app.utils package io.legado.app.utils
import android.util.Base64 import android.util.Base64
import cn.hutool.crypto.symmetric.SymmetricCrypto
import java.security.spec.AlgorithmParameterSpec
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
/** /**
* transformations https://developer.android.google.cn/reference/kotlin/javax/crypto/Cipher?hl=en * 编码工具 escape base64
*/ */
@Suppress("unused") @Suppress("unused")
object EncoderUtils { object EncoderUtils {
@ -42,302 +38,4 @@ object EncoderUtils {
return Base64.encodeToString(str.toByteArray(), flags) return Base64.encodeToString(str.toByteArray(), flags)
} }
//////////AES Start
/**
* Return the Base64-encode bytes of AES encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *AES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the Base64-encode bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptAES2Base64(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "AES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return Base64.encode(encryptAES(data, key, transformation, iv), Base64.NO_WRAP)
}
/**
* Return the bytes of AES encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *AES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptAES(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "AES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "AES", transformation!!, iv, true)
}
/**
* Return the bytes of AES decryption for Base64-encode bytes.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *AES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption for Base64-encode bytes
*/
@Throws(Exception::class)
fun decryptBase64AES(
data: ByteArray?,
key: ByteArray?,
transformation: String = "AES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return decryptAES(Base64.decode(data, Base64.NO_WRAP), key, transformation, iv)
}
/**
* Return the bytes of AES decryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *AES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption
*/
@Throws(Exception::class)
fun decryptAES(
data: ByteArray?,
key: ByteArray?,
transformation: String = "AES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "AES", transformation, iv, false)
}
//////////DES Start
/**
* Return the Base64-encode bytes of DES encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the Base64-encode bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptDES2Base64(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "DES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return Base64.encode(encryptDES(data, key, transformation, iv), Base64.NO_WRAP)
}
/**
* Return the bytes of DES encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptDES(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "DES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "DES", transformation!!, iv, true)
}
/**
* Return the bytes of DES decryption for Base64-encode bytes.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption for Base64-encode bytes
*/
@Throws(Exception::class)
fun decryptBase64DES(
data: ByteArray?,
key: ByteArray?,
transformation: String = "DES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return decryptDES(Base64.decode(data, Base64.NO_WRAP), key, transformation, iv)
}
/**
* Return the bytes of DES decryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DES/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption
*/
@Throws(Exception::class)
fun decryptDES(
data: ByteArray?,
key: ByteArray?,
transformation: String = "DES/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "DES", transformation, iv, false)
}
//////////DESede Start
/**
* Return the Base64-encode bytes of DESede encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DESede/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the Base64-encode bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptDESede2Base64(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "DESede/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return Base64.encode(encryptDESede(data, key, transformation, iv), Base64.NO_WRAP)
}
/**
* Return the bytes of DESede encryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DESede/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES encryption
*/
@Throws(Exception::class)
fun encryptDESede(
data: ByteArray?,
key: ByteArray?,
transformation: String? = "DESede/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "DESede", transformation!!, iv, true)
}
/**
* Return the bytes of DESede decryption for Base64-encode bytes.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DESede/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption for Base64-encode bytes
*/
@Throws(Exception::class)
fun decryptBase64DESede(
data: ByteArray?,
key: ByteArray?,
transformation: String = "DESede/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return decryptDESede(Base64.decode(data, Base64.NO_WRAP), key, transformation, iv)
}
/**
* Return the bytes of DESede decryption.
*
* @param data The data.
* @param key The key.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, *DESede/CBC/PKCS5Padding*.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @return the bytes of AES decryption
*/
@Throws(Exception::class)
fun decryptDESede(
data: ByteArray?,
key: ByteArray?,
transformation: String = "DESede/ECB/PKCS5Padding",
iv: ByteArray? = null
): ByteArray? {
return symmetricTemplate(data, key, "DESede", transformation, iv, false)
}
/**
* Return the bytes of symmetric encryption or decryption.
*
* @param data The data.
* @param key The key.
* @param algorithm The name of algorithm.
* @param transformation The name of the transformation,
* 加密算法/加密模式/填充类型, <i>DES/CBC/PKCS5Padding</i>.
* @param iv The buffer with the IV. The contents of the
* buffer are copied to protect against subsequent modification.
* @param isEncrypt True to encrypt, false otherwise.
* @return the bytes of symmetric encryption or decryption
*/
@Suppress("SameParameterValue")
@Throws(Exception::class)
private fun symmetricTemplate(
data: ByteArray?,
key: ByteArray?,
algorithm: String,
transformation: String,
iv: ByteArray?,
isEncrypt: Boolean
): ByteArray? {
return if (data == null || data.isEmpty() || key == null || key.isEmpty()) null
else {
//hutool support zeropadding
val keySpec = SecretKeySpec(key, algorithm)
var params: AlgorithmParameterSpec? = null
if (iv != null && !iv.isEmpty()) {
params = IvParameterSpec(iv)
}
val symmetricCrypto = SymmetricCrypto(transformation, keySpec, params)
if (isEncrypt) symmetricCrypto.encrypt(data) else symmetricCrypto.decrypt(data)
}
}
} }

@ -4,7 +4,6 @@ import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Build import android.os.Build
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.AppPattern
import okhttp3.internal.publicsuffix.PublicSuffixDatabase import okhttp3.internal.publicsuffix.PublicSuffixDatabase
import splitties.systemservices.connectivityManager import splitties.systemservices.connectivityManager
@ -13,8 +12,8 @@ import java.net.NetworkInterface
import java.net.SocketException import java.net.SocketException
import java.net.URL import java.net.URL
import java.util.* import java.util.*
import java.util.regex.Pattern
import cn.hutool.core.lang.Validator
@Suppress("unused", "MemberVisibilityCanBePrivate") @Suppress("unused", "MemberVisibilityCanBePrivate")
object NetworkUtils { object NetworkUtils {
@ -113,7 +112,7 @@ object NetworkUtils {
fun getAbsoluteURL(baseURL: String?, relativePath: String): String { fun getAbsoluteURL(baseURL: String?, relativePath: String): String {
if (baseURL.isNullOrEmpty()) return relativePath if (baseURL.isNullOrEmpty()) return relativePath
if (relativePath.isAbsUrl()) return relativePath if (relativePath.isAbsUrl()) return relativePath
if (relativePath.matches(AppPattern.dataUriRegex)) return relativePath if (relativePath.isDataUrl()) return relativePath
if (relativePath.startsWith("javascript")) return "" if (relativePath.startsWith("javascript")) return ""
var relativeUrl = relativePath var relativeUrl = relativePath
try { try {
@ -133,7 +132,7 @@ object NetworkUtils {
fun getAbsoluteURL(baseURL: URL?, relativePath: String): String { fun getAbsoluteURL(baseURL: URL?, relativePath: String): String {
if (baseURL == null) return relativePath if (baseURL == null) return relativePath
if (relativePath.isAbsUrl()) return relativePath if (relativePath.isAbsUrl()) return relativePath
if (relativePath.matches(AppPattern.dataUriRegex)) return relativePath if (relativePath.isDataUrl()) return relativePath
if (relativePath.startsWith("javascript")) return "" if (relativePath.startsWith("javascript")) return ""
var relativeUrl = relativePath var relativeUrl = relativePath
try { try {
@ -214,14 +213,14 @@ object NetworkUtils {
* @return True if the input parameter is a valid IPv4 address. * @return True if the input parameter is a valid IPv4 address.
*/ */
fun isIPv4Address(input: String?): Boolean { fun isIPv4Address(input: String?): Boolean {
return input != null && IPV4_PATTERN.matcher(input).matches() return input != null && Validator.isIpv4(input)
} }
/** /**
* Check if valid IPV6 address. * Check if valid IPV6 address.
*/ */
fun isIPv6Address(input: String?): Boolean { fun isIPv6Address(input: String?): Boolean {
return input != null && IPV6_PATTERN.matcher(input).matches() return input != null && Validator.isIpv6(input)
} }
/** /**
@ -231,19 +230,4 @@ object NetworkUtils {
return isIPv4Address(input) || isIPv6Address(input) return isIPv4Address(input) || isIPv6Address(input)
} }
/**
* Ipv4 address check.
*/
private val IPV4_PATTERN = Pattern.compile(
"^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" +
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
)
/**
* Ipv6 address check.
*/
private val IPV6_PATTERN = Pattern.compile(
"^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:)(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"
)
} }

@ -6,7 +6,9 @@ import android.icu.text.Collator
import android.icu.util.ULocale import android.icu.util.ULocale
import android.net.Uri import android.net.Uri
import android.text.Editable import android.text.Editable
import cn.hutool.core.lang.Validator
import io.legado.app.constant.AppPattern.dataUriRegex import io.legado.app.constant.AppPattern.dataUriRegex
import io.legado.app.constant.AppPattern.base64Regex
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -37,6 +39,11 @@ fun String?.isDataUrl() =
dataUriRegex.matches(it) dataUriRegex.matches(it)
} ?: false } ?: false
fun String?.isBase64() =
this?.let {
base64Regex.matches(it)
} ?: false
fun String?.isJson(): Boolean = fun String?.isJson(): Boolean =
this?.run { this?.run {
val str = this.trim() val str = this.trim()

@ -1,8 +1,12 @@
package io.legado.app.utils package io.legado.app.utils
import android.util.Base64
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.text.TextUtils.isEmpty import android.text.TextUtils.isEmpty
import cn.hutool.core.util.HexUtil
import cn.hutool.core.lang.Validator
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -287,30 +291,16 @@ object StringUtils {
return buf.toString() return buf.toString()
} }
fun byteToHexString(bytes: ByteArray?): String { /**
if (bytes == null) return "" * 自动识别Base64 Hex Utf8字符串 并转成ByteArray
val sb = StringBuilder(bytes.size * 2) */
for (b in bytes) { fun encodeStringToByteArray(str: String?): ByteArray? {
val hex = 0xff and b.toInt() return when {
if (hex < 16) { str.isNullOrBlank() -> null
sb.append('0') Validator.isHex(str) -> HexUtil.decodeHex(str)
} str.isBase64() -> Base64.decode(str, Base64.DEFAULT)
sb.append(Integer.toHexString(hex)) else -> str.encodeToByteArray()
} }
return sb.toString()
} }
fun hexStringToByte(hexString: String): ByteArray {
val hexStr = hexString.replace(" ", "")
val len = hexStr.length
val bytes = ByteArray(len / 2)
var i = 0
while (i < len) {
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
bytes[i / 2] = ((Character.digit(hexString[i], 16) shl 4) +
Character.digit(hexString[i + 1], 16)).toByte()
i += 2
}
return bytes
}
} }

@ -46,9 +46,9 @@ object SvgUtils {
} }
/////// private method /////// private method
private fun createBitmap(svg: SVG, width: Int, height: Int? = null): Bitmap { private fun createBitmap(svg: SVG, width: Int? = null, height: Int? = null): Bitmap {
val size = getSize(svg) val size = getSize(svg)
val wRatio = width.let { size.width / it } ?: -1 val wRatio = width?.let { size.width / it } ?: -1
val hRatio = height?.let { size.height / it } ?: -1 val hRatio = height?.let { size.height / it } ?: -1
//如果超出指定大小,则缩小相应的比例 //如果超出指定大小,则缩小相应的比例
val ratio = when { val ratio = when {

Loading…
Cancel
Save