CipherInputStream зависает при чтении данных - PullRequest
0 голосов
/ 07 марта 2020

Я пытаюсь зашифровать / расшифровать некоторые файлы, которые я буду читать / писать, используя FileIn/OutputStream s, переданный через CipherIn/OutputStream s. Довольно прост в концепции, и я заставил это работать, используя необработанные байтовые массивы и Cipher.doFinal. Так что я знаю, что мои параметры шифрования (размер бита, размер iv и т. Д. c) верны. (Или хотя бы функционально?)

Я могу записывать данные через CipherOutputStream просто отлично. Однако, когда я пытаюсь прочитать эти данные через CipherInputStream, они зависают бесконечно.

Единственная обнаруженная проблема , которую я обнаружил, остается без ответа и потенциально принципиально отличается от моей проблема, так как моя проблема всегда будет иметь все данные, доступные на диске, в отличие от зависимости связанной проблемы на Sockets.

Я пробовал несколько решений, наиболее очевидным из которых является изменение буфера размер (data = new byte[4096];). Я пробовал несколько значений, в том числе размер открытого текста и размер зашифрованных данных. Ни одно из этих значений не работает. Единственное решение, которое я нашел, - это вообще не использовать CipherInputStream, а вместо этого полагаться на Cipher.doFinal и Cipher.update.

Я что-то упустил? Было бы очень хорошо иметь возможность использовать CipherInputStream вместо того, чтобы заново изобретать колесо, используя Cipher.update.

SSCCE:

private static final String AES_ALG = "aes_256/gcm/nopadding";
private static final int GCM_TAG_SIZE = 128;

private static void doEncryptionTest() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
        InvalidAlgorithmParameterException, FileNotFoundException, IOException
{
    File f = new File("encrypted_random_data.dat");
    // 12-byte long iv
    byte[] iv = new byte[] {0x27, 0x51, 0x34, 0x14, -0x65, 0x4d, -0x67, 0x35, -0x63, 0x11, -0x02, -0x05};
    // 256-bit long key
    byte[] keyBytes = new byte[] {0x55, -0x7f, -0x17, -0x29, -0x68, 0x25, 0x29, 0x5f, -0x27, -0x2d, -0x4d, 0x1b,
            0x25, 0x74, 0x57, 0x35, -0x23, -0x1b, 0x12, 0x7c, 0x1, -0xf, -0x60, -0x42, 0x1c, 0x61, 0x3e, -0x5,
            -0x13, 0x31, -0x48, -0x6e};
    SecretKey key = new SecretKeySpec(keyBytes, "AES");

    OutputStream os = encryptStream(key, iv, f);

    System.out.println("generating random data...");
    // 24MB of random data
    byte[] data = new byte[25165824];
    new Random().nextBytes(data);

    System.out.println("encrypting and writing data...");
    os.write(data);

    os.close();

    InputStream is = decryptStream(key, iv, f);

    System.out.println("reading and decrypting data...");
    // read the data in 4096 byte packets
    int n;
    data = new byte[4096];
    while ((n = is.read(data)) > 0)
    {
        System.out.println("read " + n + " bytes.");
    }

    is.close();
}

private static OutputStream encryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
        NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
    GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
    Cipher enc = Cipher.getInstance(AES_ALG);
    enc.init(Cipher.ENCRYPT_MODE, key, spec);

    OutputStream os = new CipherOutputStream(new FileOutputStream(f), enc);
    return os;
}

private static InputStream decryptStream(SecretKey key, byte[] iv, File f) throws NoSuchAlgorithmException,
        NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, FileNotFoundException
{
    GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE, iv);
    Cipher dec = Cipher.getInstance(AES_ALG);
    dec.init(Cipher.DECRYPT_MODE, key, spec);

    InputStream is = new CipherInputStream(new FileInputStream(f), dec);
    return is;
}

1 Ответ

2 голосов
/ 07 марта 2020

Не зависает, просто очень медленно. CipherInputStream имеет фиксированный входной буфер размером 512, что означает, что он вызывает метод Cipher#update(byte[], int, int) с максимум 512 байтами за раз. Расшифровка вручную с большим размером буфера делает его намного быстрее.

Причина в том, что вызов update 50 000 раз с 512 байтами занимает намного больше времени, чем вызов, скажем, 400 раз с 65 килобайтами. Я не уверен, почему именно, но, кажется, есть постоянные накладные расходы, которые вы должны платить за каждый звонок update, независимо от количества данных, которые вы передаете его.

Кроме того, имейте в виду, что вы не может использовать AES GCM для расшифровки больших файлов. По замыслу, реализация Sun шифра буферизирует весь зашифрованный текст в памяти перед его расшифровкой. Вам придется разбить открытый текст на достаточно маленькие фрагменты и зашифровать каждый фрагмент по отдельности.

См. Также https://crypto.stackexchange.com/questions/20333/encryption-of-big-files-in-java-with-aes-gcm и Как получается, что тег аутентификации GCM ставится в конце поток шифров требует внутренней буферизации во время расшифровки? .

...