745 lines
27 KiB
Kotlin
745 lines
27 KiB
Kotlin
package uts.sdk.modules.laoqianjunziCrypto
|
|
|
|
import android.util.Base64
|
|
import java.io.ByteArrayOutputStream
|
|
import java.nio.charset.StandardCharsets
|
|
import java.security.GeneralSecurityException
|
|
import java.security.KeyFactory
|
|
import java.security.KeyPairGenerator
|
|
import java.security.MessageDigest
|
|
import java.security.SecureRandom
|
|
import java.security.interfaces.RSAPrivateKey
|
|
import java.security.interfaces.RSAPublicKey
|
|
import java.security.spec.PKCS8EncodedKeySpec
|
|
import java.security.spec.X509EncodedKeySpec
|
|
import javax.crypto.Cipher
|
|
import javax.crypto.Mac
|
|
import javax.crypto.spec.IvParameterSpec
|
|
import javax.crypto.spec.SecretKeySpec
|
|
|
|
data class NativeRsaBundle(val publicKey: String, val privateKey: String)
|
|
|
|
object NativeCipherCore {
|
|
private const val AES_BLOCK_SIZE = 16
|
|
private const val DES_BLOCK_SIZE = 8
|
|
private val secureRandom = SecureRandom()
|
|
|
|
fun digestHex(kind: String, plainText: String): String {
|
|
return try {
|
|
val digest = MessageDigest.getInstance(resolveDigestAlgorithm(kind))
|
|
toHex(digest.digest(plainText.toByteArray(StandardCharsets.UTF_8)))
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException("摘要失败")
|
|
}
|
|
}
|
|
|
|
fun hmacHex(kind: String, secretText: String, plainText: String): String {
|
|
return try {
|
|
val mac = Mac.getInstance(resolveHmacAlgorithm(kind))
|
|
mac.init(SecretKeySpec(secretText.toByteArray(StandardCharsets.UTF_8), mac.algorithm))
|
|
toHex(mac.doFinal(plainText.toByteArray(StandardCharsets.UTF_8)))
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException("HMAC失败")
|
|
}
|
|
}
|
|
|
|
fun encodeBase64(plainText: String): String {
|
|
return Base64.encodeToString(plainText.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP)
|
|
}
|
|
|
|
fun decodeBase64(encodedText: String): String {
|
|
return try {
|
|
String(Base64.decode(encodedText, Base64.DEFAULT), StandardCharsets.UTF_8)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("Base64无效")
|
|
}
|
|
}
|
|
|
|
fun encodeCodec(codec: String, plainText: String): String {
|
|
val bytes = plainText.toByteArray(StandardCharsets.UTF_8)
|
|
return when (codec.lowercase()) {
|
|
"utf8" -> plainText
|
|
"hex" -> toHex(bytes)
|
|
"base64" -> Base64.encodeToString(bytes, Base64.NO_WRAP)
|
|
"base64url" -> Base64.encodeToString(bytes, Base64.NO_WRAP)
|
|
.replace('+', '-')
|
|
.replace('/', '_')
|
|
.trimEnd('=')
|
|
"latin1" -> latin1FromBytes(bytes)
|
|
"utf16" -> utf16CodecFromBytes(bytes)
|
|
else -> throw IllegalArgumentException("不支持的编码类型")
|
|
}
|
|
}
|
|
|
|
fun decodeCodec(codec: String, encodedText: String): String {
|
|
return when (codec.lowercase()) {
|
|
"utf8" -> encodedText
|
|
"hex" -> String(fromHex(encodedText), StandardCharsets.UTF_8)
|
|
"base64" -> decodeBase64(encodedText)
|
|
"base64url" -> {
|
|
var normalized = encodedText.replace('-', '+').replace('_', '/')
|
|
while (normalized.length % 4 != 0) {
|
|
normalized += '='
|
|
}
|
|
decodeBase64(normalized)
|
|
}
|
|
"latin1" -> String(bytesFromLatin1(encodedText), StandardCharsets.UTF_8)
|
|
"utf16" -> String(bytesFromUtf16Codec(encodedText), StandardCharsets.UTF_8)
|
|
else -> throw IllegalArgumentException("不支持的编码类型")
|
|
}
|
|
}
|
|
|
|
fun aesEncryptText(
|
|
secretText: String,
|
|
plainText: String,
|
|
mode: String,
|
|
padding: String,
|
|
ivText: String?,
|
|
keyLength: Int?
|
|
): String {
|
|
val cipherBytes = aesEncryptBytes(
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
plainText.toByteArray(StandardCharsets.UTF_8),
|
|
mode,
|
|
padding,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8),
|
|
keyLength
|
|
)
|
|
return Base64.encodeToString(cipherBytes, Base64.NO_WRAP)
|
|
}
|
|
|
|
fun aesDecryptText(
|
|
secretText: String,
|
|
cipherText: String,
|
|
mode: String,
|
|
padding: String,
|
|
ivText: String?,
|
|
keyLength: Int?
|
|
): String {
|
|
val cipherBytes = try {
|
|
Base64.decode(cipherText, Base64.DEFAULT)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("密文Base64无效")
|
|
}
|
|
val plainBytes = aesDecryptBytes(
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
cipherBytes,
|
|
mode,
|
|
padding,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8),
|
|
keyLength
|
|
)
|
|
return String(plainBytes, StandardCharsets.UTF_8)
|
|
}
|
|
|
|
fun aesEncryptBytes(
|
|
secretBytes: ByteArray,
|
|
plainBytes: ByteArray,
|
|
mode: String,
|
|
padding: String,
|
|
ivBytes: ByteArray?,
|
|
keyLength: Int?
|
|
): ByteArray {
|
|
return cryptAes(true, secretBytes, plainBytes, mode, padding, ivBytes, keyLength)
|
|
}
|
|
|
|
fun aesDecryptBytes(
|
|
secretBytes: ByteArray,
|
|
cipherBytes: ByteArray,
|
|
mode: String,
|
|
padding: String,
|
|
ivBytes: ByteArray?,
|
|
keyLength: Int?
|
|
): ByteArray {
|
|
return cryptAes(false, secretBytes, cipherBytes, mode, padding, ivBytes, keyLength)
|
|
}
|
|
|
|
fun tripleDesEncryptText(secretText: String, plainText: String, mode: String, ivText: String?): String {
|
|
val encrypted = cryptTripleDes(
|
|
true,
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
plainText.toByteArray(StandardCharsets.UTF_8),
|
|
mode,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8)
|
|
)
|
|
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
|
}
|
|
|
|
fun tripleDesDecryptText(secretText: String, cipherText: String, mode: String, ivText: String?): String {
|
|
val cipherBytes = try {
|
|
Base64.decode(cipherText, Base64.DEFAULT)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("密文Base64无效")
|
|
}
|
|
val decrypted = cryptTripleDes(
|
|
false,
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
cipherBytes,
|
|
mode,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8)
|
|
)
|
|
return String(decrypted, StandardCharsets.UTF_8)
|
|
}
|
|
|
|
fun desEncryptText(secretText: String, plainText: String, mode: String, padding: String, ivText: String?): String {
|
|
val encrypted = cryptDes(
|
|
true,
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
plainText.toByteArray(StandardCharsets.UTF_8),
|
|
mode,
|
|
padding,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8)
|
|
)
|
|
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
|
}
|
|
|
|
fun desDecryptText(secretText: String, cipherText: String, mode: String, padding: String, ivText: String?): String {
|
|
val cipherBytes = try {
|
|
Base64.decode(cipherText, Base64.DEFAULT)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("密文Base64无效")
|
|
}
|
|
val decrypted = cryptDes(
|
|
false,
|
|
secretText.toByteArray(StandardCharsets.UTF_8),
|
|
cipherBytes,
|
|
mode,
|
|
padding,
|
|
ivText?.toByteArray(StandardCharsets.UTF_8)
|
|
)
|
|
return String(decrypted, StandardCharsets.UTF_8)
|
|
}
|
|
|
|
fun rc4EncryptToHex(secretText: String, plainText: String): String {
|
|
return toHex(rc4(secretText.toByteArray(StandardCharsets.UTF_8), plainText.toByteArray(StandardCharsets.UTF_8)))
|
|
}
|
|
|
|
fun rc4DecryptFromHex(secretText: String, cipherHexText: String): String {
|
|
val plainBytes = rc4(secretText.toByteArray(StandardCharsets.UTF_8), fromHex(cipherHexText))
|
|
return String(plainBytes, StandardCharsets.UTF_8)
|
|
}
|
|
|
|
fun createRsaBundle(keySize: Int): NativeRsaBundle {
|
|
return try {
|
|
val generator = KeyPairGenerator.getInstance("RSA")
|
|
generator.initialize(keySize)
|
|
val pair = generator.generateKeyPair()
|
|
val publicPem = toPem("PUBLIC KEY", pair.public.encoded)
|
|
val privatePem = toPem("PRIVATE KEY", pair.private.encoded)
|
|
NativeRsaBundle(publicPem, privatePem)
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException("RSA生成失败")
|
|
}
|
|
}
|
|
|
|
fun rsaEncrypt(publicKey: String, plainText: String, outputKind: String?): String {
|
|
return try {
|
|
val rsaKey = parsePublicKey(publicKey)
|
|
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
|
|
cipher.init(Cipher.ENCRYPT_MODE, rsaKey)
|
|
val encrypted = rsaProcess(cipher, plainText.toByteArray(StandardCharsets.UTF_8), rsaEncryptChunkSize(rsaKey), "RSA加密失败")
|
|
if (isHexOutput(outputKind)) toHex(encrypted) else Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException("RSA加密失败")
|
|
}
|
|
}
|
|
|
|
fun rsaDecrypt(privateKey: String, cipherText: String, outputKind: String?): String {
|
|
return try {
|
|
val rsaKey = parsePrivateKey(privateKey)
|
|
val cipherBytes = if (isHexOutput(outputKind)) {
|
|
fromHex(cipherText)
|
|
} else {
|
|
try {
|
|
Base64.decode(cipherText, Base64.DEFAULT)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("RSA密文无效")
|
|
}
|
|
}
|
|
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
|
|
cipher.init(Cipher.DECRYPT_MODE, rsaKey)
|
|
val decrypted = rsaProcess(cipher, cipherBytes, rsaDecryptChunkSize(rsaKey), "RSA解密失败")
|
|
String(decrypted, StandardCharsets.UTF_8)
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException("RSA解密失败")
|
|
}
|
|
}
|
|
|
|
private fun cryptAes(
|
|
encrypt: Boolean,
|
|
secretBytes: ByteArray,
|
|
inputBytes: ByteArray,
|
|
mode: String,
|
|
padding: String,
|
|
ivBytes: ByteArray?,
|
|
keyLength: Int?
|
|
): ByteArray {
|
|
try {
|
|
val normalizedMode = resolveAesMode(mode)
|
|
val normalizedPadding = resolvePadding(padding)
|
|
val key = SecretKeySpec(normalizeAesKey(secretBytes, keyLength), "AES")
|
|
val data = if (encrypt) padBytes(inputBytes, AES_BLOCK_SIZE, normalizedPadding) else inputBytes
|
|
if (!encrypt && data.isEmpty()) {
|
|
return ByteArray(0)
|
|
}
|
|
val cipher = Cipher.getInstance("AES/$normalizedMode/NoPadding")
|
|
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, AES_BLOCK_SIZE, normalizedMode))
|
|
val output = cipher.doFinal(data)
|
|
return if (encrypt) output else unpadBytes(output, AES_BLOCK_SIZE, normalizedPadding)
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: GeneralSecurityException) {
|
|
throw IllegalStateException(if (encrypt) "AES加密失败" else "AES解密失败")
|
|
}
|
|
}
|
|
|
|
private fun cryptTripleDes(
|
|
encrypt: Boolean,
|
|
secretBytes: ByteArray,
|
|
inputBytes: ByteArray,
|
|
mode: String,
|
|
ivBytes: ByteArray?
|
|
): ByteArray {
|
|
try {
|
|
val normalizedMode = resolveTripleDesMode(mode)
|
|
val key = SecretKeySpec(normalizeTripleDesKey(secretBytes), "DESede")
|
|
val data = if (encrypt) padBytes(inputBytes, DES_BLOCK_SIZE, "PKCS7") else inputBytes
|
|
val cipher = Cipher.getInstance("DESede/$normalizedMode/NoPadding")
|
|
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, DES_BLOCK_SIZE, normalizedMode))
|
|
val output = cipher.doFinal(data)
|
|
return if (encrypt) output else unpadBytes(output, DES_BLOCK_SIZE, "PKCS7")
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: GeneralSecurityException) {
|
|
throw IllegalStateException(if (encrypt) "3DES加密失败" else "3DES解密失败")
|
|
}
|
|
}
|
|
|
|
private fun cryptDes(
|
|
encrypt: Boolean,
|
|
secretBytes: ByteArray,
|
|
inputBytes: ByteArray,
|
|
mode: String,
|
|
padding: String,
|
|
ivBytes: ByteArray?
|
|
): ByteArray {
|
|
try {
|
|
val normalizedMode = resolveDesMode(mode)
|
|
val normalizedPadding = resolvePadding(padding)
|
|
val key = SecretKeySpec(normalizeDesKey(secretBytes), "DES")
|
|
val data = if (encrypt) padBytes(inputBytes, DES_BLOCK_SIZE, normalizedPadding) else inputBytes
|
|
if (!encrypt && data.isEmpty()) {
|
|
return ByteArray(0)
|
|
}
|
|
val cipher = Cipher.getInstance("DES/$normalizedMode/NoPadding")
|
|
initCipher(cipher, encrypt, key, normalizedMode, normalizeIv(ivBytes, DES_BLOCK_SIZE, normalizedMode))
|
|
val output = cipher.doFinal(data)
|
|
return if (encrypt) output else unpadBytes(output, DES_BLOCK_SIZE, normalizedPadding)
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: GeneralSecurityException) {
|
|
throw IllegalStateException(if (encrypt) "DES加密失败" else "DES解密失败")
|
|
}
|
|
}
|
|
|
|
private fun initCipher(cipher: Cipher, encrypt: Boolean, key: SecretKeySpec, mode: String, ivBytes: ByteArray?) {
|
|
if (mode == "ECB") {
|
|
cipher.init(if (encrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE, key)
|
|
} else {
|
|
val actualIv = ivBytes ?: throw IllegalArgumentException("IV不能为空")
|
|
cipher.init(if (encrypt) Cipher.ENCRYPT_MODE else Cipher.DECRYPT_MODE, key, IvParameterSpec(actualIv))
|
|
}
|
|
}
|
|
|
|
private fun resolveDigestAlgorithm(kind: String): String {
|
|
return when (kind.lowercase()) {
|
|
"md5" -> "MD5"
|
|
"sha1" -> "SHA-1"
|
|
"sha256" -> "SHA-256"
|
|
"sha512" -> "SHA-512"
|
|
else -> throw IllegalArgumentException("不支持的摘要算法")
|
|
}
|
|
}
|
|
|
|
private fun resolveHmacAlgorithm(kind: String): String {
|
|
return when (kind.lowercase()) {
|
|
"sha1" -> "HmacSHA1"
|
|
"sha256" -> "HmacSHA256"
|
|
"sha512" -> "HmacSHA512"
|
|
else -> throw IllegalArgumentException("不支持的HMAC算法")
|
|
}
|
|
}
|
|
|
|
private fun resolveAesMode(mode: String): String {
|
|
return when (mode.uppercase()) {
|
|
"ECB", "CBC", "CFB", "CTR", "OFB" -> mode.uppercase()
|
|
// CTRGladman 在 Android 端按标准 CTR 变换兼容映射处理。
|
|
"CTRGLADMAN" -> "CTR"
|
|
else -> throw IllegalArgumentException("不支持的AES模式")
|
|
}
|
|
}
|
|
|
|
private fun resolveTripleDesMode(mode: String): String {
|
|
return when (mode.uppercase()) {
|
|
"ECB", "CBC" -> mode.uppercase()
|
|
else -> throw IllegalArgumentException("不支持的3DES模式")
|
|
}
|
|
}
|
|
|
|
private fun resolveDesMode(mode: String): String {
|
|
return when (mode.uppercase()) {
|
|
"ECB", "CBC", "CFB", "CTR", "OFB" -> mode.uppercase()
|
|
"CTRGLADMAN" -> "CTR"
|
|
else -> throw IllegalArgumentException("不支持的DES模式")
|
|
}
|
|
}
|
|
|
|
private fun resolvePadding(padding: String): String {
|
|
return when (padding.uppercase()) {
|
|
"PKCS7", "ANSI_X923", "ISO_10126", "ISO_97971", "NONE", "ZERO" -> padding.uppercase()
|
|
else -> throw IllegalArgumentException("不支持的填充")
|
|
}
|
|
}
|
|
|
|
private fun normalizeAesKey(secretBytes: ByteArray, keyLength: Int?): ByteArray {
|
|
val keySize = when (keyLength) {
|
|
null -> inferAesKeySize(secretBytes.size)
|
|
128, 192, 256 -> keyLength / 8
|
|
else -> throw IllegalArgumentException("AES密钥长度无效")
|
|
}
|
|
return fitBytes(secretBytes, keySize)
|
|
}
|
|
|
|
private fun inferAesKeySize(size: Int): Int {
|
|
return when {
|
|
size <= 16 -> 16
|
|
size <= 24 -> 24
|
|
size <= 32 -> 32
|
|
else -> throw IllegalArgumentException("AES密钥长度无效")
|
|
}
|
|
}
|
|
|
|
private fun normalizeTripleDesKey(secretBytes: ByteArray): ByteArray {
|
|
val source = fitBytes(secretBytes, 24)
|
|
if (secretBytes.size <= 16) {
|
|
System.arraycopy(source, 0, source, 16, 8)
|
|
}
|
|
return source
|
|
}
|
|
|
|
private fun normalizeDesKey(secretBytes: ByteArray): ByteArray {
|
|
if (secretBytes.isEmpty()) {
|
|
throw IllegalArgumentException("密钥不能为空")
|
|
}
|
|
return fitBytes(secretBytes, 8)
|
|
}
|
|
|
|
private fun normalizeIv(ivBytes: ByteArray?, expectedSize: Int, mode: String): ByteArray? {
|
|
if (mode == "ECB") return null
|
|
val bytes = ivBytes ?: return null
|
|
return fitBytes(bytes, expectedSize)
|
|
}
|
|
|
|
private fun fitBytes(source: ByteArray, targetSize: Int): ByteArray {
|
|
val output = ByteArray(targetSize)
|
|
val copySize = minOf(source.size, targetSize)
|
|
System.arraycopy(source, 0, output, 0, copySize)
|
|
return output
|
|
}
|
|
|
|
private fun padBytes(input: ByteArray, blockSize: Int, padding: String): ByteArray {
|
|
if (padding == "NONE") {
|
|
if (input.size % blockSize != 0) {
|
|
throw IllegalArgumentException("数据长度不是块大小整数倍")
|
|
}
|
|
return input
|
|
}
|
|
val padSize = blockSize - (input.size % blockSize).let { if (it == 0) blockSize else it }
|
|
val actualPadSize = if (padSize == 0) blockSize else padSize
|
|
val output = ByteArray(input.size + actualPadSize)
|
|
System.arraycopy(input, 0, output, 0, input.size)
|
|
when (padding) {
|
|
"PKCS7" -> fillPad(output, input.size, actualPadSize, actualPadSize.toByte())
|
|
"ANSI_X923" -> output[output.lastIndex] = actualPadSize.toByte()
|
|
"ISO_10126" -> {
|
|
val randomBytes = ByteArray(actualPadSize - 1)
|
|
if (randomBytes.isNotEmpty()) {
|
|
secureRandom.nextBytes(randomBytes)
|
|
System.arraycopy(randomBytes, 0, output, input.size, randomBytes.size)
|
|
}
|
|
output[output.lastIndex] = actualPadSize.toByte()
|
|
}
|
|
"ISO_97971" -> {
|
|
output[input.size] = 0x80.toByte()
|
|
}
|
|
"ZERO" -> {
|
|
}
|
|
else -> throw IllegalArgumentException("不支持的填充")
|
|
}
|
|
return output
|
|
}
|
|
|
|
private fun fillPad(output: ByteArray, start: Int, count: Int, value: Byte) {
|
|
for (i in start until start + count) {
|
|
output[i] = value
|
|
}
|
|
}
|
|
|
|
private fun unpadBytes(input: ByteArray, blockSize: Int, padding: String): ByteArray {
|
|
if (padding == "NONE") {
|
|
return input
|
|
}
|
|
if (input.isEmpty() || input.size % blockSize != 0) {
|
|
throw IllegalArgumentException("密文长度无效")
|
|
}
|
|
val dataLength = when (padding) {
|
|
"PKCS7" -> {
|
|
val padSize = input.last().toInt() and 0xFF
|
|
validatePadSize(padSize, blockSize)
|
|
for (i in input.size - padSize until input.size) {
|
|
if ((input[i].toInt() and 0xFF) != padSize) {
|
|
throw IllegalArgumentException("填充无效")
|
|
}
|
|
}
|
|
input.size - padSize
|
|
}
|
|
"ANSI_X923" -> {
|
|
val padSize = input.last().toInt() and 0xFF
|
|
validatePadSize(padSize, blockSize)
|
|
for (i in input.size - padSize until input.lastIndex) {
|
|
if (input[i].toInt() != 0) {
|
|
throw IllegalArgumentException("填充无效")
|
|
}
|
|
}
|
|
input.size - padSize
|
|
}
|
|
"ISO_10126" -> {
|
|
val padSize = input.last().toInt() and 0xFF
|
|
validatePadSize(padSize, blockSize)
|
|
input.size - padSize
|
|
}
|
|
"ISO_97971" -> {
|
|
var index = input.lastIndex
|
|
while (index >= 0 && input[index].toInt() == 0) {
|
|
index -= 1
|
|
}
|
|
if (index < 0 || input[index] != 0x80.toByte()) {
|
|
throw IllegalArgumentException("填充无效")
|
|
}
|
|
index
|
|
}
|
|
"ZERO" -> {
|
|
var index = input.size
|
|
while (index > 0 && input[index - 1].toInt() == 0) {
|
|
index -= 1
|
|
}
|
|
index
|
|
}
|
|
else -> throw IllegalArgumentException("不支持的填充")
|
|
}
|
|
return input.copyOf(dataLength)
|
|
}
|
|
|
|
private fun validatePadSize(padSize: Int, blockSize: Int) {
|
|
if (padSize <= 0 || padSize > blockSize) {
|
|
throw IllegalArgumentException("填充无效")
|
|
}
|
|
}
|
|
|
|
private fun rc4(secretBytes: ByteArray, inputBytes: ByteArray): ByteArray {
|
|
if (secretBytes.isEmpty()) {
|
|
throw IllegalArgumentException("RC4密钥不能为空")
|
|
}
|
|
val s = IntArray(256) { it }
|
|
var j = 0
|
|
for (i in 0 until 256) {
|
|
j = (j + s[i] + (secretBytes[i % secretBytes.size].toInt() and 0xFF)) and 0xFF
|
|
val tmp = s[i]
|
|
s[i] = s[j]
|
|
s[j] = tmp
|
|
}
|
|
val output = ByteArray(inputBytes.size)
|
|
var i = 0
|
|
j = 0
|
|
for (index in inputBytes.indices) {
|
|
i = (i + 1) and 0xFF
|
|
j = (j + s[i]) and 0xFF
|
|
val tmp = s[i]
|
|
s[i] = s[j]
|
|
s[j] = tmp
|
|
val keyByte = s[(s[i] + s[j]) and 0xFF]
|
|
output[index] = (inputBytes[index].toInt() xor keyByte).toByte()
|
|
}
|
|
return output
|
|
}
|
|
|
|
private fun parsePublicKey(publicKey: String): RSAPublicKey {
|
|
try {
|
|
val keyBytes = decodePem(publicKey, "PUBLIC KEY")
|
|
val keySpec = X509EncodedKeySpec(keyBytes)
|
|
return KeyFactory.getInstance("RSA").generatePublic(keySpec) as RSAPublicKey
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("公钥无效")
|
|
}
|
|
}
|
|
|
|
private fun parsePrivateKey(privateKey: String): RSAPrivateKey {
|
|
try {
|
|
val keyBytes = decodePem(privateKey, "PRIVATE KEY")
|
|
val keySpec = PKCS8EncodedKeySpec(keyBytes)
|
|
return KeyFactory.getInstance("RSA").generatePrivate(keySpec) as RSAPrivateKey
|
|
} catch (e: IllegalArgumentException) {
|
|
throw e
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("私钥无效")
|
|
}
|
|
}
|
|
|
|
private fun decodePem(pemText: String, label: String): ByteArray {
|
|
val header = "-----BEGIN $label-----"
|
|
val footer = "-----END $label-----"
|
|
val trimmed = pemText.trim()
|
|
if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) {
|
|
throw IllegalArgumentException("PEM格式无效")
|
|
}
|
|
val content = trimmed
|
|
.removePrefix(header)
|
|
.removeSuffix(footer)
|
|
.replace("\\s".toRegex(), "")
|
|
return try {
|
|
Base64.decode(content, Base64.DEFAULT)
|
|
} catch (e: Exception) {
|
|
throw IllegalArgumentException("PEM内容无效")
|
|
}
|
|
}
|
|
|
|
private fun toPem(label: String, keyBytes: ByteArray): String {
|
|
val base64 = Base64.encodeToString(keyBytes, Base64.NO_WRAP)
|
|
val builder = StringBuilder()
|
|
builder.append("-----BEGIN ").append(label).append("-----\n")
|
|
var index = 0
|
|
while (index < base64.length) {
|
|
val end = minOf(index + 64, base64.length)
|
|
builder.append(base64, index, end).append('\n')
|
|
index = end
|
|
}
|
|
builder.append("-----END ").append(label).append("-----")
|
|
return builder.toString()
|
|
}
|
|
|
|
private fun rsaEncryptChunkSize(key: RSAPublicKey): Int {
|
|
return (key.modulus.bitLength() + 7) / 8 - 11
|
|
}
|
|
|
|
private fun rsaDecryptChunkSize(key: RSAPrivateKey): Int {
|
|
return (key.modulus.bitLength() + 7) / 8
|
|
}
|
|
|
|
private fun rsaProcess(cipher: Cipher, input: ByteArray, chunkSize: Int, failureMessage: String): ByteArray {
|
|
if (chunkSize <= 0) {
|
|
throw IllegalArgumentException("RSA密钥无效")
|
|
}
|
|
val output = ByteArrayOutputStream()
|
|
var offset = 0
|
|
try {
|
|
while (offset < input.size) {
|
|
val size = minOf(chunkSize, input.size - offset)
|
|
output.write(cipher.doFinal(input, offset, size))
|
|
offset += size
|
|
}
|
|
return output.toByteArray()
|
|
} catch (e: Exception) {
|
|
throw IllegalStateException(failureMessage)
|
|
} finally {
|
|
output.close()
|
|
}
|
|
}
|
|
|
|
private fun isHexOutput(outputKind: String?): Boolean {
|
|
return outputKind?.lowercase() == "hex"
|
|
}
|
|
|
|
private fun latin1FromBytes(bytes: ByteArray): String {
|
|
val chars = CharArray(bytes.size)
|
|
for (i in bytes.indices) {
|
|
chars[i] = (bytes[i].toInt() and 0xFF).toChar()
|
|
}
|
|
return String(chars)
|
|
}
|
|
|
|
private fun bytesFromLatin1(text: String): ByteArray {
|
|
val output = ByteArray(text.length)
|
|
for (i in text.indices) {
|
|
output[i] = (text[i].code and 0xFF).toByte()
|
|
}
|
|
return output
|
|
}
|
|
|
|
private fun utf16CodecFromBytes(bytes: ByteArray): String {
|
|
if (bytes.isEmpty()) {
|
|
return ""
|
|
}
|
|
val chars = CharArray((bytes.size + 1) / 2)
|
|
var byteIndex = 0
|
|
var charIndex = 0
|
|
while (byteIndex < bytes.size) {
|
|
val high = bytes[byteIndex].toInt() and 0xFF
|
|
val low = if (byteIndex + 1 < bytes.size) bytes[byteIndex + 1].toInt() and 0xFF else 0
|
|
chars[charIndex] = ((high shl 8) or low).toChar()
|
|
byteIndex += 2
|
|
charIndex += 1
|
|
}
|
|
return String(chars)
|
|
}
|
|
|
|
private fun bytesFromUtf16Codec(text: String): ByteArray {
|
|
val output = ByteArray(text.length * 2)
|
|
var index = 0
|
|
for (charValue in text) {
|
|
val code = charValue.code
|
|
output[index] = ((code ushr 8) and 0xFF).toByte()
|
|
output[index + 1] = (code and 0xFF).toByte()
|
|
index += 2
|
|
}
|
|
return output
|
|
}
|
|
|
|
private fun toHex(bytes: ByteArray): String {
|
|
val chars = CharArray(bytes.size * 2)
|
|
val digits = "0123456789abcdef"
|
|
for (i in bytes.indices) {
|
|
val value = bytes[i].toInt() and 0xFF
|
|
chars[i * 2] = digits[value ushr 4]
|
|
chars[i * 2 + 1] = digits[value and 0x0F]
|
|
}
|
|
return String(chars)
|
|
}
|
|
|
|
private fun fromHex(hexText: String): ByteArray {
|
|
val normalized = hexText.trim()
|
|
if (normalized.length % 2 != 0) {
|
|
throw IllegalArgumentException("Hex无效")
|
|
}
|
|
val output = ByteArray(normalized.length / 2)
|
|
for (i in output.indices) {
|
|
val start = i * 2
|
|
val value = normalized.substring(start, start + 2).toIntOrNull(16)
|
|
?: throw IllegalArgumentException("Hex无效")
|
|
output[i] = value.toByte()
|
|
}
|
|
return output
|
|
}
|
|
}
|