使用Facebook conceal解密文件时出现OutOfMemoryException异常

4
我正在开发一个Android应用程序,需要将视频保存在SD卡中,并且不能被转移,这就是为什么我会根据需要使用Facebook Conceal进行加密和解密,如果视频大小较小,则完美运行。但是,当我尝试对大于10MB的视频文件进行加密和解密时,在GenyMotion 2.3.7上会崩溃,并显示OutOfMemoryException,这意味着我正在耗尽分配给我的应用程序的堆内存,这无法处理,但必须予以预防。
已尝试:
- Apache Common Utils IO包 - 各种IO Utils
Facebook Conceal:在解密时表示。
 You must read the entire stream to completion.
 The verification is done at the end of the stream.
 Thus not reading till the end of the stream will cause
 a security bug. For safety, you should not
 use any of the data until it's been fully read or throw
 away the data if an exception occurs.

我正在调用的代码使用 Facebook Conceal 进行加密和解密:

加密:

public void startEncryption() {
    // Creates a new Crypto object with default implementations of
    // a key chain as well as native library.
    // Check for whether the crypto functionality is available
    // This might fail if android does not load libaries correctly.
    if (!crypto.isAvailable()) {
        return;
    }
    OutputStream fileStream;
    try {
        File mEncryptedFile = new File(mPlainFile.getPath().substring(0,
                mPlainFile.getPath().length() - 4)
                + "_encrypted"
                + mPlainFile.getPath().substring(
                        mPlainFile.getPath().length() - 4,
                        mPlainFile.getPath().length()));

        fileStream = new BufferedOutputStream(new FileOutputStream(
                mEncryptedFile));

        // Creates an output stream which encrypts the data as
        // it is written to it and writes it out to the file.
        OutputStream outputStream;
        outputStream = crypto.getCipherOutputStream(fileStream, entity);
        outputStream.write(FileUtils.readFileToByteArray(mPlainFile));
        // fileStream.flush();
        // fileStream.close();
        // outputStream.flush();
        // outputStream.close();
        // outputStream.flush();
        File mRenameTo = new File(mPlainFile.getPath());
        mPlainFile.delete();
        mEncryptedFile.renameTo(mRenameTo);
    } catch (FileNotFoundException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (CryptoInitializationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (KeyChainException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Decryption :

public String startDecryption() {
    // Get the file to which ciphertext has been written.
    try {
        FileInputStream fileStream = new FileInputStream(mPlainFile);

        // Creates an input stream which decrypts the data as
        // it is read from it.
        InputStream inputStream;
        inputStream = crypto.getCipherInputStream(fileStream, entity);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // org.apache.commons.io.output.ByteArrayOutputStream out = new
        // org.apache.commons.io.output.ByteArrayOutputStream(1024);
        // Read into a byte array.
        // int read;
        // byte[] buffer = new byte[1024];
        // // You must read the entire stream to completion.
        // // The verification is done at the end of the stream.
        // // Thus not reading till the end of the stream will cause
        // // a security bug.
        // int i = 0;
        // while ((read = inputStream.read(buffer)) != -1) {
        // out.write(buffer, 0, read);
        // Log.i(TAG, "bytearrayoutputstream "+i++ + " "+read + " " +
        // buffer.length + " "+out.size());
        // }

        mDecryptedFile = new File(mPlainFile.getPath().substring(0,
                mPlainFile.getPath().length() - 4)
                + "_decrypted"
                + (mPlainFile.getPath().substring(mPlainFile.getPath()
                        .length() - 4, mPlainFile.getPath().length())));

        OutputStream outputStream = new FileOutputStream(mDecryptedFile);
        // IOUtils.copy(inputStream, outputStream);

        try {
            final byte[] buffer = new byte[1024];
            int read;

            while ((read = inputStream.read(buffer)) != -1)
                outputStream.write(buffer, 0, read);

            outputStream.flush();
        } catch (Exception e) {

        } finally {
            outputStream.close();
        }

        // out.writeTo(outputStream);
        // out.flush();
        // out.close();

        // fileStream.close();
        inputStream.close();
        // outputStream.flush();
        outputStream.close();
        return mDecryptedFile.getPath();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (CryptoInitializationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (KeyChainException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

有没有可以解决并加密和解密大型视频文件的解决方案?
2个回答

2

看起来你是将整个文件读入一个字节数组中。你应该创建一个输入流并将其提供给加密引擎。

所以,代码应该改为:

 outputStream.write(FileUtils.readFileToByteArray(mPlainFile));

应该与你的解密循环类似。
BufferedInputStream  bis = new BufferedInputStream(mPlainFile);
InputStream inputStream = crypto.getCipherInputStream(
                             bis,
                             entity);
while ((read = inputStream.read(buffer)) != -1) {
   outputStream.write(buffer, 0, read);
}

不确定您最终得到的是否是这个。我从文档中提取了加密解码部分。我认为这证实了他们的意图是说必须读取流直到结束。


2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Lostboy
正如Facebook在其文档中明确指出的那样:“您必须完全读取整个流。验证是在流的末尾完成的。因此,如果不读取到流的末尾,将会导致安全漏洞。为了安全起见,在完全读取数据之前不应使用任何数据,或者在发生异常时丢弃数据。”,可以将其表述为Stream没有解密文件。 - Vikalp Patel
@Bruce的回答应该是可行的。Facebook说必须完整地读取整个流,但不必将整个文件加载到内存中。这意味着如果发生任何错误,您应该删除已生成的文件,因为它不安全。 - Anton Krosnev
@AntonK。谢谢AntonK。我已经尝试阅读它,就像Bruce所说的那样,但不幸的是它导致了无法解密表单。如果您知道其他方法,那将非常有帮助 :) - Vikalp Patel

0

正如Bruce所强调的,加密瓶颈会导致在解密时出现OutOfMemoryException。因此,这里是我在加密和解密时执行的代码,不再导致OutOfMemoryException

加密:

fileStream = new BufferedOutputStream(new FileOutputStream(mEncryptedFile));
OutputStream outputStream;
outputStream = crypto.getCipherOutputStream(fileStream, entity);
int read;
byte[] buffer = new byte[1024];
BufferedInputStream  bis = new BufferedInputStream(newFileInputStream(mPlainFile));
while ((read = bis.read(buffer)) != -1) {
       outputStream.write(buffer, 0, read);
    }
outputStream.close();
bis.close();

解密:

InputStream inputStream;
inputStream = crypto.getCipherInputStream(fileStream, entity);
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream outputStream = new FileOutputStream(mDecryptedFile);
BufferedInputStream bis = new BufferedInputStream(inputStream);
int mRead;
byte[] mBuffer = new byte[1024];
while ((mRead = bis.read(mBuffer)) != -1) {
   outputStream.write(mBuffer, 0, mRead);
    }
bis.close();
out.writeTo(outputStream);
inputStream.close();
outputStream.close();
out.close();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接