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) } }