121 lines
5.0 KiB
Kotlin
121 lines
5.0 KiB
Kotlin
package top.tangyh.lamp.filing.utils
|
||
|
||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||
import java.nio.charset.StandardCharsets
|
||
import java.security.MessageDigest
|
||
import java.security.SecureRandom
|
||
import java.util.*
|
||
import javax.crypto.Cipher
|
||
import javax.crypto.spec.GCMParameterSpec
|
||
import javax.crypto.spec.SecretKeySpec
|
||
|
||
private val logger = KotlinLogging.logger {}
|
||
|
||
/**
|
||
* 任务加密工具类
|
||
* 使用 licence + fingerprint 作为密钥对任务数据进行 AES-256-GCM 对称加密
|
||
*
|
||
* GCM 模式提供认证加密,比 ECB 模式更安全
|
||
* 加密数据格式:IV(12字节) + 加密数据 + 认证标签(16字节)
|
||
*/
|
||
object TaskEncryptionUtil {
|
||
|
||
private const val ALGORITHM = "AES"
|
||
private const val TRANSFORMATION = "AES/GCM/NoPadding"
|
||
private const val GCM_IV_LENGTH = 12 // GCM 推荐使用 12 字节 IV
|
||
private const val GCM_TAG_LENGTH = 16 // GCM 认证标签长度(128位)
|
||
private const val KEY_LENGTH = 32 // AES-256 密钥长度(256位 = 32字节)
|
||
|
||
private val secureRandom = SecureRandom()
|
||
|
||
/**
|
||
* 使用 licence + fingerprint 加密任务数据(AES-256-GCM)
|
||
* @param data 待加密的数据(JSON字符串)
|
||
* @param licence 授权码
|
||
* @param fingerprint 硬件指纹
|
||
* @return Base64编码的加密数据(包含IV + 加密数据 + 认证标签)
|
||
*/
|
||
fun encrypt(data: String, licence: String, fingerprint: String): String {
|
||
return try {
|
||
// 使用 licence + fingerprint 生成密钥
|
||
val key = generateKey(licence, fingerprint)
|
||
|
||
// 生成随机 IV(12字节)
|
||
val iv = ByteArray(GCM_IV_LENGTH)
|
||
secureRandom.nextBytes(iv)
|
||
|
||
// 创建加密器
|
||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||
val parameterSpec = GCMParameterSpec(GCM_TAG_LENGTH * 8, iv) // 标签长度以位为单位
|
||
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec)
|
||
|
||
// 加密数据
|
||
val encryptedBytes = cipher.doFinal(data.toByteArray(StandardCharsets.UTF_8))
|
||
|
||
// 组合:IV + 加密数据(包含认证标签)
|
||
val combined = ByteArray(iv.size + encryptedBytes.size)
|
||
System.arraycopy(iv, 0, combined, 0, iv.size)
|
||
System.arraycopy(encryptedBytes, 0, combined, iv.size, encryptedBytes.size)
|
||
|
||
// 返回 Base64 编码的加密数据
|
||
Base64.getEncoder().encodeToString(combined)
|
||
} catch (e: Exception) {
|
||
logger.error(e) { "AES-256-GCM加密任务数据失败" }
|
||
throw RuntimeException("加密任务数据失败: ${e.message}", e)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用 licence + fingerprint 解密任务数据(AES-256-GCM)
|
||
* @param encryptedData Base64编码的加密数据(包含IV + 加密数据 + 认证标签)
|
||
* @param licence 授权码
|
||
* @param fingerprint 硬件指纹
|
||
* @return 解密后的数据(JSON字符串)
|
||
*/
|
||
fun decrypt(encryptedData: String, licence: String, fingerprint: String): String {
|
||
return try {
|
||
// 使用 licence + fingerprint 生成密钥
|
||
val key = generateKey(licence, fingerprint)
|
||
|
||
// Base64 解码
|
||
val combined = Base64.getDecoder().decode(encryptedData)
|
||
|
||
// 分离 IV 和加密数据
|
||
if (combined.size < GCM_IV_LENGTH) {
|
||
throw IllegalArgumentException("加密数据格式错误:数据长度不足")
|
||
}
|
||
|
||
val iv = combined.sliceArray(0 until GCM_IV_LENGTH)
|
||
val cipherText = combined.sliceArray(GCM_IV_LENGTH until combined.size)
|
||
|
||
// 创建解密器
|
||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||
val parameterSpec = GCMParameterSpec(GCM_TAG_LENGTH * 8, iv)
|
||
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec)
|
||
|
||
// 解密数据(GCM 会自动验证认证标签)
|
||
val decryptedBytes = cipher.doFinal(cipherText)
|
||
|
||
// 返回解密后的字符串
|
||
String(decryptedBytes, StandardCharsets.UTF_8)
|
||
} catch (e: Exception) {
|
||
logger.error(e) { "AES-256-GCM解密任务数据失败" }
|
||
throw RuntimeException("解密任务数据失败: ${e.message}", e)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用 licence + fingerprint 生成 AES-256 密钥(256位 = 32字节)
|
||
* 使用 SHA-256 哈希的全部32字节作为密钥
|
||
*/
|
||
private fun generateKey(licence: String, fingerprint: String): SecretKeySpec {
|
||
val combined = "$licence$fingerprint"
|
||
val digest = MessageDigest.getInstance("SHA-256")
|
||
val hash = digest.digest(combined.toByteArray(StandardCharsets.UTF_8))
|
||
|
||
// 使用全部32字节作为 AES-256 密钥
|
||
return SecretKeySpec(hash, ALGORITHM)
|
||
}
|
||
}
|
||
|