Kai*_*Kai 4 java encryption exception heap-memory out-of-memory
我试图使用 javax.crypto 下的类和用于输入/输出的文件流来实现加密/解密程序。为了限制内存使用,我使用-Xmx256m参数运行。
它适用于较小文件的加密和解密。但是当解密一个大文件(1G大小)时,出现内存不足的异常:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at com.sun.crypto.provider.GaloisCounterMode.decrypt(GaloisCounterMode.java:505)
at com.sun.crypto.provider.CipherCore.update(CipherCore.java:782)
at com.sun.crypto.provider.CipherCore.update(CipherCore.java:667)
at com.sun.crypto.provider.AESCipher.engineUpdate(AESCipher.java:380)
at javax.crypto.Cipher.update(Cipher.java:1831)
at javax.crypto.CipherOutputStream.write(CipherOutputStream.java:166)
Run Code Online (Sandbox Code Playgroud)
这是解密代码:
private final int _readSize = 0x10000;//64k
...
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(gcmTagSize, iv);
Key keySpec = new SecretKeySpec(key, keyParts[0]);
Cipher decCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decCipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
try (InputStream fileInStream = Files.newInputStream(inputEncryptedFile);
OutputStream fileOutStream = Files.newOutputStream(outputDecryptedFile)) {
try (CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutStream, decCipher)) {
long count = 0L;
byte[] buffer = new byte[_readSize];
int n;
for (; (n = fileInStream.read(buffer)) != -1; count += (long) n) {
cipherOutputStream.write(buffer, 0, n);
}
}
}
Run Code Online (Sandbox Code Playgroud)
gcmTagSize 和 iv 等关键参数是从关键文件中读取的,对于较小的文件(例如大小约为 50M 的文件)来说它可以正常工作。
据我了解,每次只有64k数据传递给解密,为什么它会耗尽堆内存?我怎样才能避免这种情况?
编辑:
实际上我尝试过使用 4k 作为缓冲区大小,但由于同样的异常而失败。
编辑2:
经过更多测试,它可以处理的最大文件大小约为堆大小的 1/4。例如,如果设置-Xmx256m,大于64M的文件将无法解密。
这似乎是 GCM 模式实施的问题。我不确定你是否可以解决这个问题。
如果您查看堆栈跟踪:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at com.sun.crypto.provider.GaloisCounterMode.decrypt(GaloisCounterMode.java:505)
Run Code Online (Sandbox Code Playgroud)
ByteArrayOutputStream从 内部写入时会发生内存不足错误GaloisCounterMode。您使用 a FileOutputStream,因此要么您没有显示正确的代码,要么这ByteArrayStream是在内部使用的。
如果您查看GaloisCounterMode 的源代码,您会发现它定义了一个内部ByteArrayOutputStream(它实际上定义了两个,但我认为这就是问题所在):
// buffer for storing input in decryption, not used for encryption
private ByteArrayOutputStream ibuffer = null;
Run Code Online (Sandbox Code Playgroud)
然后,稍后,它将字节写入该流。注意代码注释。
int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
processAAD();
if (len > 0) {
// store internally until decryptFinal is called because
// spec mentioned that only return recovered data after tag
// is successfully verified
ibuffer.write(in, inOfs, len);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该缓冲区直到 才重置decryptFinal()。
编辑:看看这个 CSx 答案,看起来 GCM 需要缓冲整个流。如果您有大文件且内存不足,那么这将是一个非常糟糕的选择。
我认为最好的解决方案是切换到 CBC 模式。
| 归档时间: |
|
| 查看次数: |
1750 次 |
| 最近记录: |