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. 396
      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/#/)
* AES
> transformation默认实现AES/ECB/PKCS5Padding
```
java.aesDecodeToString(str: String, key: String, transformation: String, iv: String)
java.aesBase64DecodeToString(str: String, key: String, transformation: String, iv: String)
java.aesEncodeToString(str: String, key: String, transformation: String, iv: String)
java.aesEncodeToBase64String(str: String, key: String, transformation: String, iv: String)
```
* DES
> transformation默认实现DES/ECB/PKCS5Padding
```
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?
* 对称加密AES/DES/TripleDES
> AES transformation默认实现AES/ECB/PKCS5Padding
> DES transformation默认实现DES/ECB/PKCS5Padding
> TripleDES tansformation默认实现DESede/ECB/PKCS5Padding
> 内部实现为cn.hutool.crypto 解密加密接口支持ByteArray|Base64String|HexString|InputStream
> 输入参数key iv 支持ByteArray|Base64String|HexString|Utf8String
```
//解密为ByteArray 字符串
java.createSymmetricCrypto(transformation, key, iv).decrypt(data)
java.createSymmetricCrypto(transformation, key, iv).decryptStr(data)
//加密为ByteArray Base64字符 HEX字符
java.createSymmetricCrypto(transformation, key, iv).encrypt(data)
java.createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
java.createSymmetricCrypto(transformation, key, iv).encryptHex(data)
```
* 摘要
> MD5 SHA-1 SHA-224 SHA-256 SHA-384 SHA-512

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

@ -13,6 +13,8 @@ object AppPattern {
//dataURL图片类型
val dataUriRegex = Regex("data:.*?;base64,(.*)")
//Base64字符串
val base64Regex = Regex("^[a-zA-Z0-9\\+\\/=]+$")
val nameRegex = Regex("\\s+作\\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 com.script.SimpleBindings
import cn.hutool.crypto.symmetric.AES
import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.data.entities.rule.RowUi
@ -119,10 +120,7 @@ interface BaseSource : JsExtensions {
try {
val key = AppConst.androidId.encodeToByteArray(0, 16)
val cache = CacheManager.get("userInfo_${getKey()}") ?: return null
val encodeBytes = Base64.decode(cache, Base64.DEFAULT)
val decodeBytes = EncoderUtils.decryptAES(encodeBytes, key)
?: return null
return String(decodeBytes)
return AES(key).decryptStr(cache)
} catch (e: Exception) {
AppLog.put("获取登陆信息出错", e)
return null
@ -139,8 +137,7 @@ interface BaseSource : JsExtensions {
fun putLoginInfo(info: String): Boolean {
return try {
val key = (AppConst.androidId).encodeToByteArray(0, 16)
val encodeBytes = EncoderUtils.encryptAES(info.toByteArray(), key)
val encodeStr = Base64.encodeToString(encodeBytes, Base64.DEFAULT)
val encodeStr = AES(key).encryptBase64(info)
CacheManager.put("userInfo_${getKey()}", encodeStr)
true
} catch (e: Exception) {

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

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

@ -1,13 +1,9 @@
package io.legado.app.utils
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")
object EncoderUtils {
@ -42,302 +38,4 @@ object EncoderUtils {
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.os.Build
import io.legado.app.constant.AppLog
import io.legado.app.constant.AppPattern
import okhttp3.internal.publicsuffix.PublicSuffixDatabase
import splitties.systemservices.connectivityManager
@ -13,8 +12,8 @@ import java.net.NetworkInterface
import java.net.SocketException
import java.net.URL
import java.util.*
import java.util.regex.Pattern
import cn.hutool.core.lang.Validator
@Suppress("unused", "MemberVisibilityCanBePrivate")
object NetworkUtils {
@ -113,7 +112,7 @@ object NetworkUtils {
fun getAbsoluteURL(baseURL: String?, relativePath: String): String {
if (baseURL.isNullOrEmpty()) return relativePath
if (relativePath.isAbsUrl()) return relativePath
if (relativePath.matches(AppPattern.dataUriRegex)) return relativePath
if (relativePath.isDataUrl()) return relativePath
if (relativePath.startsWith("javascript")) return ""
var relativeUrl = relativePath
try {
@ -133,7 +132,7 @@ object NetworkUtils {
fun getAbsoluteURL(baseURL: URL?, relativePath: String): String {
if (baseURL == null) return relativePath
if (relativePath.isAbsUrl()) return relativePath
if (relativePath.matches(AppPattern.dataUriRegex)) return relativePath
if (relativePath.isDataUrl()) return relativePath
if (relativePath.startsWith("javascript")) return ""
var relativeUrl = relativePath
try {
@ -214,14 +213,14 @@ object NetworkUtils {
* @return True if the input parameter is a valid IPv4 address.
*/
fun isIPv4Address(input: String?): Boolean {
return input != null && IPV4_PATTERN.matcher(input).matches()
return input != null && Validator.isIpv4(input)
}
/**
* Check if valid IPV6 address.
*/
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)
}
/**
* 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.net.Uri
import android.text.Editable
import cn.hutool.core.lang.Validator
import io.legado.app.constant.AppPattern.dataUriRegex
import io.legado.app.constant.AppPattern.base64Regex
import java.io.File
import java.util.*
@ -37,6 +39,11 @@ fun String?.isDataUrl() =
dataUriRegex.matches(it)
} ?: false
fun String?.isBase64() =
this?.let {
base64Regex.matches(it)
} ?: false
fun String?.isJson(): Boolean =
this?.run {
val str = this.trim()

@ -1,8 +1,12 @@
package io.legado.app.utils
import android.util.Base64
import android.annotation.SuppressLint
import android.text.TextUtils.isEmpty
import cn.hutool.core.util.HexUtil
import cn.hutool.core.lang.Validator
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
@ -287,30 +291,16 @@ object StringUtils {
return buf.toString()
}
fun byteToHexString(bytes: ByteArray?): String {
if (bytes == null) return ""
val sb = StringBuilder(bytes.size * 2)
for (b in bytes) {
val hex = 0xff and b.toInt()
if (hex < 16) {
sb.append('0')
}
sb.append(Integer.toHexString(hex))
/**
* 自动识别Base64 Hex Utf8字符串 并转成ByteArray
*/
fun encodeStringToByteArray(str: String?): ByteArray? {
return when {
str.isNullOrBlank() -> null
Validator.isHex(str) -> HexUtil.decodeHex(str)
str.isBase64() -> Base64.decode(str, Base64.DEFAULT)
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 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 wRatio = width.let { size.width / it } ?: -1
val wRatio = width?.let { size.width / it } ?: -1
val hRatio = height?.let { size.height / it } ?: -1
//如果超出指定大小,则缩小相应的比例
val ratio = when {

Loading…
Cancel
Save