You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IOS平台基于LibGDX/robovm实现ChaCha加解密遇问题求助

iOS平台基于LibGDX+RoboVM实现ChaCha字节数组加解密方案

问题本质

你碰到的核心问题是RoboVM在iOS打包时,对BouncyCastle(BC)类的处理逻辑和Android存在差异:

  • 调用Cipher.getInstance()失败:iOS系统默认安全提供者不支持ChaCha算法,即便BC已注册,RoboVM也可能未正确映射BC的Cipher实现
  • ChaChaEngine类找不到:RoboVM的链接器会默认剔除未被直接引用的类,BC的ChaCha相关类大概率被误删

可行解决方案

方案1:修复BouncyCastle依赖的RoboVM打包配置

  1. 确保项目引入完整的BC依赖,比如org.bouncycastle:bcprov-jdk15on
  2. 在项目的robovm.xml配置文件中添加强制链接规则,避免ChaCha相关类被打包时剔除:
<forceLinkClasses>
    <pattern>org.bouncycastle.crypto.engines.ChaChaEngine</pattern>
    <pattern>org.bouncycastle.crypto.params.KeyParameter</pattern>
    <pattern>org.bouncycastle.crypto.params.ParametersWithIV</pattern>
</forceLinkClasses>
  1. 直接使用ChaChaEngine实现加解密(Kotlin示例):
import org.bouncycastle.crypto.engines.ChaChaEngine
import org.bouncycastle.crypto.params.KeyParameter
import org.bouncycastle.crypto.params.ParametersWithIV

fun chachaEncryptDecrypt(input: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
    val engine = ChaChaEngine()
    val keyParam = KeyParameter(key)
    val params = ParametersWithIV(keyParam, iv)
    engine.init(true, params) // true为加密,false为解密(流密码加解密逻辑完全一致,可复用)
    
    val output = ByteArray(input.size)
    engine.processBytes(input, 0, input.size, output, 0)
    return output
}

注意:ChaCha密钥长度必须是16字节(128位)或32字节(256位),IV长度固定为12字节(96位)

方案2:手动实现ChaCha算法(无第三方依赖)

如果依赖配置仍有问题,可以直接手动实现ChaCha核心逻辑,彻底规避库依赖问题:

class ChaCha20(private val key: ByteArray, private val iv: ByteArray) {
    private val state = IntArray(16)

    init {
        require(key.size == 16 || key.size == 32) { "密钥长度必须为16或32字节" }
        require(iv.size == 12) { "IV长度必须为12字节" }
        
        // 初始化状态:固定常量 + 密钥 + IV + 计数器(初始为0)
        state[0] = 0x61707865
        state[1] = 0x3320646e
        state[2] = 0x79622d32
        state[3] = 0x6b206574
        for (i in 0 until key.size/4) {
            state[4 + i] = byteArrayToInt(key, i*4)
        }
        state[12] = byteArrayToInt(iv, 0)
        state[13] = byteArrayToInt(iv, 4)
        state[14] = byteArrayToInt(iv, 8)
        state[15] = 0 // 计数器初始值
    }

    private fun byteArrayToInt(bytes: ByteArray, offset: Int): Int {
        return (bytes[offset].toInt() and 0xff) or
                ((bytes[offset+1].toInt() and 0xff) shl 8) or
                ((bytes[offset+2].toInt() and 0xff) shl 16) or
                ((bytes[offset+3].toInt() and 0xff) shl 24)
    }

    private fun intToByteArray(i: Int, bytes: ByteArray, offset: Int) {
        bytes[offset] = (i and 0xff).toByte()
        bytes[offset+1] = ((i shr 8) and 0xff).toByte()
        bytes[offset+2] = ((i shr 16) and 0xff).toByte()
        bytes[offset+3] = ((i shr 24) and 0xff).toByte()
    }

    private fun quarterRound(a: Int, b: Int, c: Int, d: Int) {
        state[a] += state[b]; state[d] = state[d] xor state[a]; state[d] = state[d] rotl 16
        state[c] += state[d]; state[b] = state[b] xor state[c]; state[b] = state[b] rotl 12
        state[a] += state[b]; state[d] = state[d] xor state[a]; state[d] = state[d] rotl 8
        state[c] += state[d]; state[b] = state[b] xor state[c]; state[b] = state[b] rotl 7
    }

    private infix fun Int.rotl(k: Int): Int = (this shl k) or (this ushr (32 - k))

    private fun generateBlock(output: ByteArray, offset: Int) {
        val working = state.copyOf()
        repeat(10) {
            // 列轮操作
            quarterRound(0, 4, 8, 12)
            quarterRound(1, 5, 9, 13)
            quarterRound(2, 6, 10, 14)
            quarterRound(3, 7, 11, 15)
            // 对角线轮操作
            quarterRound(0, 5, 10, 15)
            quarterRound(1, 6, 11, 12)
            quarterRound(2, 7, 8, 13)
            quarterRound(3, 4, 9, 14)
        }
        for (i in state.indices) {
            intToByteArray(state[i] + working[i], output, offset + i*4)
        }
        // 计数器递增,处理块溢出
        state[15]++
        if (state[15] == 0) {
            state[14]++
        }
    }

    fun process(input: ByteArray): ByteArray {
        val output = ByteArray(input.size)
        val block = ByteArray(64)
        var blockOffset = 0
        var inputOffset = 0

        while (inputOffset < input.size) {
            if (blockOffset == 0) {
                generateBlock(block, 0)
            }
            val remaining = minOf(input.size - inputOffset, 64 - blockOffset)
            for (i in 0 until remaining) {
                output[inputOffset + i] = (input[inputOffset + i] xor block[blockOffset + i])
            }
            inputOffset += remaining
            blockOffset += remaining
            if (blockOffset == 64) {
                blockOffset = 0
            }
        }
        return output
    }
}

// 使用示例
fun chachaManualEncryptDecrypt(input: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
    val chacha = ChaCha20(key, iv)
    return chacha.process(input)
}

说明:这是标准ChaCha20实现,加解密共用process方法(流密码特性),密钥和IV要求与BC版本完全一致。

内容的提问来源于stack exchange,提问作者ilya_kas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 11:04:52