Java Шифрование больших файлов - PullRequest
0 голосов
/ 14 июля 2020

Я пытаюсь расшифровать файлы с помощью Java и ниже работают коды. Однако при расшифровке больших файлов будет исключение OutOfMemory. Я попытался изменить его на cipher.update, но программа зависла без ответа.

Как мне изменить это с doFinal на update?

public File decryptDataFile(File inputFile, File outputFile, File keyFile, String correlationId) {
        
        try {
            
            Security.addProvider(new BouncyCastleProvider());
            
            String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
            
            byte[] byteInput = this.getFileInBytes(inputFile);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
            
            byte[] salt = new byte[8];
            System.arraycopy(byteInput, 8, salt, 0, 8);
            SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
            cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));

            byte[] data = cipher.doFinal(byteInput, 16, byteInput.length-16);
            
            OutputStream os = new FileOutputStream(outputFile);
            os.write(data);
            os.close();
            
            if(outputFile.exists()) {
                return outputFile;
            } else {
                return null;
            }
            
        } catch (IOException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) {
            logger.WriteLog(appConfig.getPlatform(), "INFO", alsConfig.getProjectCode(), correlationId, alsConfig.getFunctionId(), "SCAN_DECRYPT", e.getClass().getCanonicalName() + " - " + e.getMessage() );
            return null;
        }

    }

Мой нерабочий версия:

Security.addProvider(new BouncyCastleProvider());
            
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
            
byte[] byteInput = this.getFileInBytes(inputFile);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
            
byte[] salt = new byte[8];
System.arraycopy(byteInput, 8, salt, 0, 8);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));
            
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
            
CipherInputStream cis = new CipherInputStream(fis, cipher);
            
int b;
byte[] d = new byte[8];
while((b = cis.read(d)) != -1) {
   fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
if(outputFile.exists()) {
    return outputFile;
} else {
    return null;
}

1 Ответ

4 голосов
/ 15 июля 2020

Предисловие: Я не смог расшифровать вашим исходным методом файл, который был зашифрован с помощью вашей openssl-команды

openssl enc -aes-256-cbc -e -salt -in ${tarFile} -out ${encTarFile} -pass file:./${KEY_RANDOM}

, но следующий метод должен декодировать даже большие файлы, аналогичные вашему исходному методу - я тестировал файлы размером до 1 ГБ.

Изменить: Что касается оператора OpenSSL, стоит упомянуть, что, начиная с версии 1.1.0, дайджест по умолчанию изменился с MD5 на SHA256, поэтому для более высоких В версиях для совместимости с кодом Java необходимо явно указать параметр -md MD5. (спасибо @Topaco).

Пожалуйста, имейте в виду, что меня не интересуют правильные пути к файлам для

new FileInputStream(inputFile.toPath().toString())
and
new FileOutputStream(outputFile.toPath().toString())

так как я работаю локально и со своей папкой, возможно, вам придется изменить код, чтобы «найти» ваши файлы. Кроме того, в этом примере нет обработки исключений.

Строка кода

byte[] ibuf = new byte[8096];

определяет используемый буфер - больший буфер ускоряет дешифрование, но потребляет больше памяти (8096 означает 8096 байт по сравнению с 1 ГБ при чтении полного файла в память и возникновении ошибки нехватки памяти).

public static File decryptDataFileBuffered(File inputFile, File outputFile, File keyFile, String correlationId) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
        Security.addProvider(new BouncyCastleProvider());
        String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
        byte[] salt = new byte[8];
        byte[] salted = new byte[8]; // text SALTED__
        try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
             FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
        {
            byte[] ibuf = new byte[8096]; // thats the buffer used - larger is faster
            int len;
            in.read(salted);
            in.read(salt);
            SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
            SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
            System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            while ((len = in.read(ibuf)) != -1) {
                byte[] obuf = cipher.update(ibuf, 0, len);
                if (obuf != null)
                    out.write(obuf);
            }
            byte[] obuf = cipher.doFinal();
            if (obuf != null)
                out.write(obuf);
         } catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
            e.printStackTrace();
         }
        if (outputFile.exists()) {
            return outputFile;
        } else {
            return null;
        }
    }

Edit2: Как прокомментировал @Topaco использование CipherInput / OutputStream сокращает код и делает его более читабельным, поэтому вот код:

public static File decryptDataFileBufferedCipherInputStream (File inputFile, File outputFile, File keyFile, String correlationId) throws
            IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException
    {
        Security.addProvider(new BouncyCastleProvider());
        String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
        byte[] salt = new byte[8];
        byte[] salted = new byte[8]; // text SALTED__
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
             CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
             FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
        {
            byte[] buffer = new byte[8192];
            in.read(salted);
            in.read(salt);
            SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
            SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
            System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            int nread;
            while ((nread = cipherInputStream.read(buffer)) > 0) {
                out.write(buffer, 0, nread);
            }
            out.flush();
        }
        if (outputFile.exists()) {
            return outputFile;
        } else {
            return null;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...