使用RNCryptor库AES256实现图片加解密后无法加载求助
解决AES256JNCryptor加密解密后图片无法打开的问题
你遇到的核心问题是:用Android的AES256JNCryptor加密图片后,无论是Android本地解密还是iOS用RNCryptor解密,得到的图片文件大小和原文件一致,但无法正常加载显示。结合你的代码和尝试的方案,我整理了几个关键问题点和修复方法:
1. 文件读取不完整是最可能的元凶
你的代码里直接用buf.read(fileBytes, 0, fileBytes.size)来读取文件,但InputStream.read()不会保证一次性读取所有字节——它返回的是实际读取的字节数,如果文件较大或者IO缓冲不足,只会读取部分内容。这会导致加密/解密的字节数组不完整,最终生成损坏的图片文件。
修复方式:替换为可靠的文件读取逻辑
写一个辅助方法确保读取完整的文件字节:
private fun readFileCompletely(inputFile: File): ByteArray { val inputStream = BufferedInputStream(FileInputStream(inputFile)) val byteStream = ByteArrayOutputStream() val buffer = ByteArray(1024) // 用1KB缓冲区分块读取 var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) { byteStream.write(buffer, 0, bytesRead) } inputStream.close() return byteStream.toByteArray() }
然后在加密和解密方法里,用这个方法替换原有的buf.read(fileBytes...)逻辑:
// 加密时读取原文件 val fileBytes = readFileCompletely(inputFile) // 解密时读取加密文件 val fileBytes = readFileCompletely(inputFile)
2. 跨平台加密参数要严格对齐
你iOS端用的是RNCryptor,Android端用的是AES256JNCryptor,要确保两者的加密格式完全一致:
- 确认
AES256JNCryptor使用的是RNCryptor v3格式(iOS的RNCryptor默认是v3版本,大部分实现都兼容这个版本) - 两端密码要完全一致:你iOS代码里用的是
hashKey.description,Android端直接写死了"master".toCharArray(),要保证两边的密码字符串(包括编码,比如UTF-8)完全相同,否则解密会失败或者得到损坏的数据。
3. 避免不必要的Bitmap转换操作
你尝试过把解密后的字节转成Bitmap再压缩写入,这一步完全多余,还可能损坏图片:
- 如果原图片是PNG格式,强行用JPEG压缩会丢失透明通道,导致图片损坏
- Bitmap转换过程中可能会因为解码错误生成无效的图片数据
直接写入解密后的原始字节数组即可,不要做额外的格式转换。
4. 确保文件写入完整可靠
替换你的文件写入逻辑,确保所有字节都被写入并刷新:
private fun writeBytesToFile(bytes: ByteArray, outputFile: File) { // 先删除已存在的文件,避免写入叠加 if (outputFile.exists()) outputFile.delete() val outputStream = BufferedOutputStream(FileOutputStream(outputFile)) outputStream.write(bytes) outputStream.flush() // 强制把缓冲区内容写入磁盘 outputStream.close() }
在解密后调用这个方法写入文件:
val decryptedFileBytes = aeS256JNCryptor.decryptData(fileBytes, "master".toCharArray()) writeBytesToFile(decryptedFileBytes, file)
完整修复后的示例代码
加密方法
fun encryptFile(inputFile: File): File { val aesCryptor = AES256JNCryptor() val outputFile = File( Environment.getExternalStorageDirectory().toString(), "Encrypted_${inputFile.name}" ) try { val rawFileBytes = readFileCompletely(inputFile) val encryptedBytes = aesCryptor.encryptData(rawFileBytes, "master".toCharArray()) writeBytesToFile(encryptedBytes, outputFile) } catch (e: Exception) { e.printStackTrace() } return outputFile }
解密方法
fun decryptFile(inputFile: File): File { val aesCryptor = AES256JNCryptor() val outputFile = File( Environment.getExternalStorageDirectory().toString(), "Decrypted_${inputFile.name}" ) try { val encryptedBytes = readFileCompletely(inputFile) val decryptedBytes = aesCryptor.decryptData(encryptedBytes, "master".toCharArray()) writeBytesToFile(decryptedBytes, outputFile) } catch (e: Exception) { e.printStackTrace() } return outputFile } // 辅助读取方法 private fun readFileCompletely(inputFile: File): ByteArray { val inputStream = BufferedInputStream(FileInputStream(inputFile)) val byteStream = ByteArrayOutputStream() val buffer = ByteArray(1024) var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) { byteStream.write(buffer, 0, bytesRead) } inputStream.close() return byteStream.toByteArray() } // 辅助写入方法 private fun writeBytesToFile(bytes: ByteArray, outputFile: File) { if (outputFile.exists()) outputFile.delete() val outputStream = BufferedOutputStream(FileOutputStream(outputFile)) outputStream.write(bytes) outputStream.flush() outputStream.close() }
额外需要检查的点
- 存储权限:Android 10及以上版本,直接写入
Environment.getExternalStorageDirectory()需要申请MANAGE_EXTERNAL_STORAGE权限,或者改用MediaStore来保存文件,否则可能出现写入不完整的情况。 - 文件格式验证:可以用Hex编辑器查看解密后的文件头,比如JPEG的文件头是
0xFFD8,PNG是89 50 4E 47,确认文件头是否正确,判断是否是解密过程出了问题。
内容的提问来源于stack exchange,提问作者Alok Omkar
相关产品推荐
相关产品推荐

