gpt4 book ai didi

java - 使用密码解密大文件时出现内存不足异常

转载 作者:行者123 更新时间:2023-12-05 01:36:37 25 4
gpt4 key购买 nike

我试图使用 javax.crypto 下的类和用于输入/输出的文件流来实现加密/解密程序。为了限制内存使用,我使用 -Xmx256m 参数运行。

它适用于较小文件的加密和解密。但是在解密超大文件(1G大小)时,会出现out of memory异常:

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)

解密代码如下:

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);
}
}
}

像 gcmTagSize 和 iv 这样的关键参数是从一个关键文件中读取的,它适用于较小的文件,比如一些大约 50M 的文件。

据我了解,每次只有 64k 数据传递给 decipher,为什么会用完堆内存?我怎样才能避免这种情况?

编辑:

实际上我尝试过使用 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)

GaloisCounterMode 中写入 ByteArrayOutputStream 时发生内存不足错误。您使用了 FileOutputStream,因此您没有显示正确的代码,或者此 ByteArrayStream 已在内部使用。

如果您查看 source for GaloisCounterMode您会看到它定义了一个内部 ByteArrayOutputStream(它实际上定义了两个,但我认为这就是问题所在):

    // buffer for storing input in decryption, not used for encryption
private ByteArrayOutputStream ibuffer = null;

然后,稍后,它将字节写入此流。注意代码注释。

    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;
}

该缓冲区直到 decryptFinal() 才被重置。


编辑:查看this CSx answer看起来 GCM 需要缓冲整个流。如果您有大文件且内存不足,那将是一个非常糟糕的选择。

我认为您最好的解决方案是切换到 CBC 模式。

关于java - 使用密码解密大文件时出现内存不足异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61792534/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com