diff --git a/app/src/main/assets/help/jsHelp.md b/app/src/main/assets/help/jsHelp.md index 41de61d2e..d9f70de63 100644 --- a/app/src/main/assets/help/jsHelp.md +++ b/app/src/main/assets/help/jsHelp.md @@ -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 diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index bd386dbc3..d20be7d60 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -17,6 +17,7 @@ * web服务和朗读服务添加唤醒锁 * 修复搜索禁用书源的bug * 修复本地书籍很慢的问题 +* 更改内置对称加密函数,请看文档 **2022/10/14** diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index 731a7296a..cfecd8bcc 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -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+著") diff --git a/app/src/main/java/io/legado/app/data/entities/BaseSource.kt b/app/src/main/java/io/legado/app/data/entities/BaseSource.kt index 0c4c186f7..f73d2977e 100644 --- a/app/src/main/java/io/legado/app/data/entities/BaseSource.kt +++ b/app/src/main/java/io/legado/app/data/entities/BaseSource.kt @@ -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) { diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 40423239a..91686a122 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -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 } //******************消息摘要/散列消息鉴别码************************// diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index f35ca3965..a2d71f570 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -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()) diff --git a/app/src/main/java/io/legado/app/utils/EncoderUtils.kt b/app/src/main/java/io/legado/app/utils/EncoderUtils.kt index a9adacb30..fec85ff2c 100644 --- a/app/src/main/java/io/legado/app/utils/EncoderUtils.kt +++ b/app/src/main/java/io/legado/app/utils/EncoderUtils.kt @@ -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, - * 加密算法/加密模式/填充类型, DES/CBC/PKCS5Padding. - * @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) - } - } - - } diff --git a/app/src/main/java/io/legado/app/utils/NetworkUtils.kt b/app/src/main/java/io/legado/app/utils/NetworkUtils.kt index 5b8b4d906..e913acf10 100644 --- a/app/src/main/java/io/legado/app/utils/NetworkUtils.kt +++ b/app/src/main/java/io/legado/app/utils/NetworkUtils.kt @@ -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*$" - ) - } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt index f4f941047..d4ce56ab2 100644 --- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -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() diff --git a/app/src/main/java/io/legado/app/utils/StringUtils.kt b/app/src/main/java/io/legado/app/utils/StringUtils.kt index 4ae38fe46..4c5dda84c 100644 --- a/app/src/main/java/io/legado/app/utils/StringUtils.kt +++ b/app/src/main/java/io/legado/app/utils/StringUtils.kt @@ -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 - } } diff --git a/app/src/main/java/io/legado/app/utils/SvgUtils.kt b/app/src/main/java/io/legado/app/utils/SvgUtils.kt index 3c98547b4..d1906e46e 100644 --- a/app/src/main/java/io/legado/app/utils/SvgUtils.kt +++ b/app/src/main/java/io/legado/app/utils/SvgUtils.kt @@ -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 {