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

基于Room与Keystore的加密密码存储:离线认证解密-1异常排查及方案

异常解决与离线认证优化方案

一、解密-1异常的核心原因与修复

1. 流读取逻辑缺陷

你的decryptText方法存在两个致命问题:

  • inputStream.read()返回-1代表流已耗尽,直接将此值作为ivSize会创建长度为-1的数组,触发异常;
  • read(iv)和read(encryptedBytes)无法保证一次性读取完整数据,InputStream.read(byte[])仅返回实际读取的字节数,必须循环读取直到填满目标数组。

修复后的decryptText代码:

fun decryptText(inputStream: InputStream): ByteArray {
    return inputStream.use {
        // 读取IV长度,先校验有效性
        val ivSize = it.read()
        if (ivSize == -1) throw IOException("无效加密数据:缺失IV长度")
        
        val iv = ByteArray(ivSize)
        var bytesRead = 0
        // 循环读取IV至完整
        while (bytesRead < ivSize) {
            val read = it.read(iv, bytesRead, ivSize - bytesRead)
            if (read == -1) throw IOException("无效加密数据:IV不完整")
            bytesRead += read
        }

        // 读取加密数据长度
        val encryptedBytesSize = it.read()
        if (encryptedBytesSize == -1) throw IOException("无效加密数据:缺失加密内容长度")
        
        val encryptedBytes = ByteArray(encryptedBytesSize)
        bytesRead = 0
        // 循环读取加密数据至完整
        while (bytesRead < encryptedBytesSize) {
            val read = it.read(encryptedBytes, bytesRead, encryptedBytesSize - bytesRead)
            if (read == -1) throw IOException("无效加密数据:加密内容不完整")
            bytesRead += read
        }

        getDecryptCipherForIv(iv).doFinal(encryptedBytes)
    }
}

2. 加密逻辑冗余问题

你提到要存储哈希后的密码,但当前代码对哈希后的密码再次进行AES加密——哈希是不可逆操作,根本不需要解密。认证时只需将用户输入的密码重新哈希,再与存储的哈希值对比即可,这能直接避免加密解密环节的所有异常。

如果坚持要加密存储哈希值,需确保encryptCipher与getDecryptCipherForIv(iv)的算法参数完全匹配(如模式GCM/CBC、填充方式NoPadding/PKCS7Padding等),且动态盐值必须与加密时的盐一致(盐值需随加密数据一同存储,不能每次生成不同值)。

二、离线认证流程优化方案

1. 密码存储逻辑修正

  • 只哈希,不加密:使用PBKDF2WithHmacSHA256或Argon2等慢哈希算法处理密码,搭配用户唯一的盐值(盐值需与哈希值一起存储)。示例代码:
fun hashPassword(password: String, salt: ByteArray): ByteArray {
    val spec = PBEKeySpec(password.toCharArray(), salt, 100000, 256)
    val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
    return factory.generateSecret(spec).encoded
}

// 认证校验
fun verifyPassword(inputPassword: String, storedHash: ByteArray, storedSalt: ByteArray): Boolean {
    val inputHash = hashPassword(inputPassword, storedSalt)
    return MessageDigest.isEqual(inputHash, storedHash)
}
  • 邮箱可加密存储(避免明文泄露),但无需哈希——认证时先解密邮箱,再与用户输入的邮箱直接对比。

2. Room数据库存储优化

  • 将盐值单独作为字段存储(若使用PBKDF2),无需将哈希值、邮箱存为BLOB,可转为Base64字符串存储(更易调试,规避BLOB操作的潜在问题):
@Entity(tableName = "account_credentials")
data class AccountCredentials(
    @PrimaryKey val uuid: String,
    val firebaseId: String?,
    val hashedPassword: String, // Base64编码的哈希值
    val email: String, // 加密后Base64编码的邮箱
    val salt: String // Base64编码的盐值
)

3. Keystore使用优化

  • 确保Keystore中生成的AES密钥持久化,且算法参数正确(如AES/GCM/NoPadding,GCM模式需配套IV和标签);
  • 避免每次加密都创建新的CryptographicManager实例,复用实例减少资源消耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:46:02