0

I am writing java program to encrypt video files,with small video files it performs as expected however when i try on a huge file approximately 800 Megabyte,it throws an exception java.lang.OutOfMemoryError: Java heap space. I have researched on the topic getting more information about the garbage collector from here and reviewing similar problems here. According to the best chosen answer how can you "Encrypt a byte at a time": With block ciphers encryption is by the block (AES: 16-bytes)`. The contents of my source code

main.java

public static void main(String[] args) {
    String key = "Mary has one cat";
    File inputFile = new File("C:\\Users\\xyz\\Music\\test\\-.mp4");
    File encryptedFile = new File("C:\\Users\\xyz\\Music\\test\\-.mp4.encrypted");
    File decryptedFile = new File("C:\\Users\\xyz\\Music\\test\\decrypted.mp4");

    try {
        CryptoUtils.encrypt(key, inputFile, encryptedFile);
        CryptoUtils.decrypt(key, encryptedFile, decryptedFile);
    } catch (CryptoException ex) {
        System.out.println(ex.getMessage());
        ex.printStackTrace();
    }
}

CryptoUtils.java

 private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";

public static void encrypt(String key, File inputFile, File outputFile)
        throws CryptoException {
    doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}

public static void decrypt(String key, File inputFile, File outputFile)
        throws CryptoException {
    doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}

private static void doCrypto(int cipherMode, String key, File inputFile,
        File outputFile) throws CryptoException {
    try {
        Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(cipherMode, secretKey);

        FileInputStream inputStream = new FileInputStream(inputFile);
        byte[] inputBytes = new byte[(int) inputFile.length()];
        inputStream.read(inputBytes);

        byte[] outputBytes = cipher.doFinal(inputBytes);

        FileOutputStream outputStream = new FileOutputStream(outputFile);
        outputStream.write(outputBytes);

        inputStream.close();
        outputStream.close();

    } catch (NoSuchPaddingException | NoSuchAlgorithmException
            | InvalidKeyException | BadPaddingException
            | IllegalBlockSizeException | IOException ex) {
        throw new CryptoException("Error encrypting/decrypting file", ex);
    }
}

CryptoException.java

public class CryptoException extends Exception {

public CryptoException() {
}

public CryptoException(String message, Throwable throwable) {
    super(message, throwable);
}}
Cœur
  • 37,241
  • 25
  • 195
  • 267
kenn
  • 1,384
  • 12
  • 19
  • Don't read the whole file into memory, instead do the encryption block by block. – Henry Jan 05 '18 at 08:31
  • (1) Your links are not correctly formatted to be URLs (2) Java `Cipher` API handles both stream or stream-mode and block-mode ciphers with the same methods, and necessarily handles block buffering internally, so byte-at-a-time does work -- although chunk-at-a-time like Maurice's 4k is better. (3) Using any human-readable or even printable string as a (symmetric) key is weak (in any language) and fragile (in Java using defaulted charset, which varies) but that's offtopic for SO and belongs on security.SX or crypto.SX – dave_thompson_085 Jan 05 '18 at 09:40

2 Answers2

3

I think you should look into using CipherOutputStream. This way you won't need to load the file into memory:

https://docs.oracle.com/javase/9/docs/api/javax/crypto/CipherOutputStream.html

Jeroen Steenbeeke
  • 3,884
  • 5
  • 17
  • 26
3

Don't process the whole file at once: use a buffer:

try (FileInputStream inputStream = new FileInputStream(inputFile);
        FileOutputStream outputStream = new FileOutputStream(outputFile)) {
    byte[] inputBytes = new byte[4096];
    for (int n = inputStream.read(inputBytes); n > 0; n = inputStream.read(inputBytes)) {
        byte[] outputBytes = cipher.update(inputBytes, 0, n);
        outputStream.write(outputBytes);
    }

    byte[] outputBytes = cipher.doFinal();
    outputStream.write(outputBytes);
}
Maurice Perry
  • 9,261
  • 2
  • 12
  • 24