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

写入Room数据库后ByteArray不一致的问题求助

问题分析

你的问题核心出在Room类型转换器使用Json序列化Pair<ByteArray, ByteArray>:kotlinx.serialization的Json默认会将ByteArray编码为Base64字符串,虽然表面上能还原,但Json的序列化规则(如转义、格式处理)会导致二进制数据出现细微偏差。而加密解密对二进制数据的一致性要求极高,一点点偏差都会让解密后的字节流无法被ObjectInputStream正确解析,最终抛出StreamCorruptedException: invalid stream header。

另外,你加密函数中手动用copyOf补全字节到16倍数的操作也存在隐患:填充的0字节会留在解密后的数据流中,可能干扰ObjectInputStream对序列化对象的解析。


解决方案

1. 替换Room类型转换器(核心修复)

放弃Json序列化,改用Base64直接编码二进制数据,避免中间格式转换导致的字节偏差。下面提供两种可行方案:

方案一:分别编码IV和加密数据后拼接
class UserDataConverters {
    private val base64 = Base64.DEFAULT

    @TypeConverter
    fun fromTokenPair(pair: Pair<ByteArray, ByteArray>): String {
        val ivStr = String(base64.encode(pair.first))
        val dataStr = String(base64.encode(pair.second))
        // 用Base64中不会出现的分隔符拼接
        return "$ivStr|||$dataStr"
    }

    @TypeConverter
    fun toTokenPair(serializedPair: String): Pair<ByteArray, ByteArray> {
        val parts = serializedPair.split("|||")
        val ivBytes = base64.decode(parts[0].toByteArray())
        val dataBytes = base64.decode(parts[1].toByteArray())
        return Pair(ivBytes, dataBytes)
    }
}
方案二:将整个Pair序列化为ByteArray后再编码

利用你已有的ByteArray工具函数,直接序列化Pair对象:

class UserDataConverters {
    private val base64 = Base64.DEFAULT

    @TypeConverter
    fun fromTokenPair(pair: Pair<ByteArray, ByteArray>): String {
        val pairBytes = pair.toByteArray()
        return String(base64.encode(pairBytes))
    }

    @TypeConverter
    fun toTokenPair(serializedPair: String): Pair<ByteArray, ByteArray> {
        val pairBytes = base64.decode(serializedPair.toByteArray())
        return fromByteArray(pairBytes)
    }
}

2. 修正加密函数的填充逻辑

去掉手动补全字节的代码,让Cipher自动处理填充(AES默认使用PKCS5Padding,与PKCS7兼容,能正确处理非16倍数的数据流):

override fun <T : Serializable> encryptData(data: T): Pair<ByteArray, ByteArray> {
    val temp = data.toByteArray()
    // 移除手动补全代码,Cipher自动处理填充
    cipher.init(Cipher.ENCRYPT_MODE, getKey())

    val ivBytes = cipher.iv
    val encryptedArray = cipher.doFinal(temp)
    
    return Pair(ivBytes, encryptedArray)
}

验证效果

替换类型转换器并修正加密函数后,写入数据库再读取解密的流程即可正常执行,不会再出现流头错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 10:15:34