Если шифр не в состоянии кодировать, повторные попытки также не удастся - PullRequest
2 голосов
/ 08 февраля 2020

Допустим, у вас есть несколько строк, которые вы хотите зашифровать с помощью вашего открытого ключа c, а затем дешифровать, которые не связаны друг с другом. Но один из них не соответствует sh критериям (он длиннее, чем может обработать длина ключа), и вы хотите выдать ошибку и продолжить декодирование других.

Что ж, дело в том, что если выдается исключение Data must not be longer than x bytes, то также завершится с ошибкой .

Почему это происходит и как я могу предотвратить это?

I сделал понятный код, который можно использовать для воспроизведения ошибки:

Cipher encrypter;
        Cipher decrypter;

        Key pubKey;
        Key privKey;

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");

        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();

        pubKey = kp.getPublic();
        privKey = kp.getPrivate();

        encrypter = Cipher.getInstance("RSA");
        encrypter.init(Cipher.ENCRYPT_MODE, pubKey);

        decrypter = Cipher.getInstance("RSA");
        decrypter.init(Cipher.DECRYPT_MODE, privKey);

        byte[] encryptedData;
        byte[] decryptedData;


        System.out.println("Starting short test 1");
        encryptedData = encrypter.doFinal("SHORT TEST 1".getBytes());
        decryptedData = decrypter.doFinal(encryptedData);
        System.out.println(new String(decryptedData)); //SHORT TEST

        System.out.println("Starting short test 3");
        encryptedData = encrypter.doFinal("SHORT TEST 2".getBytes());
        decryptedData = decrypter.doFinal(encryptedData);
        System.out.println(new String(decryptedData)); //SHORT TEST 2

        System.out.println("Starting short test 3");
        encryptedData = encrypter.doFinal("SHORT TEST 3".getBytes());
        decryptedData = decrypter.doFinal(encryptedData);
        System.out.println(new String(decryptedData)); //SHORT TEST 3

        try {

            encryptedData = encrypter.doFinal(("LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST" +
                    " LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST" +
                    " LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST LONG TEST ").getBytes());
            decryptedData = decrypter.doFinal(encryptedData);
            System.out.println(new String(decryptedData)); // IT DOESN'T REACH HERE, WHICH IS OK

        } catch (IllegalBlockSizeException e){
            System.out.println(e.toString());  // Data must not be longer than 117 bytes (OK, fair enough, my bad)
        }

        System.out.println("Starting short test 4");
        encryptedData = encrypter.doFinal("SHORT TEST 4".getBytes());
        decryptedData = decrypter.doFinal(encryptedData);
        System.out.println(new String(decryptedData)); // THROWS THE SAME EXCEPTION THAN IN PREVIOUS TEST

Печать консоли:

Starting short test 1
SHORT TEST 1
Starting short test 3
SHORT TEST 2
Starting short test 3
SHORT TEST 3
javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
Starting short test 4
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
    at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:347)
    at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:392)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
    at main.main(main.java:66)

Ответы [ 2 ]

4 голосов
/ 08 февраля 2020

Документация doFinal () гласит, что :

Примечание: если выдается какое-либо исключение, этот объект шифрования может потребоваться сбросить, прежде чем его можно будет использовать снова.

Так как метода reset нет, я полагаю, вам нужно снова вызвать init.

2 голосов
/ 08 февраля 2020

Довольно прямой ответ m0skit0 , конечно, хорошо, но есть несколько вещей, на которые следует обратить внимание.


Следует избегать исключений в максимально возможной степени (в выпущенном коде ). Повторное шифрование одного и того же сообщения не будет работать, поскольку вы получите то же исключение, поэтому для этого конкретного сообщения у вас не будет выбора. Если вы используете гибридную криптографию (следующий пункт), то вы легко можете избежать этой проблемы.


RSA не предназначен для передачи больших объемов данных. Узнайте, как можно объединить AES / GCM и RSA / OAEP для создания гибридной криптосистемы , которая может эффективно шифровать / дешифровать любой объем данных.


Экземпляры класса Cipher как правило легкий . Они относительно дешевы для создания и инициализации и не несут много состояния . Таким образом, простое создание нового экземпляра намного менее подвержено ошибкам, чем повторное использование старого.

Это означает, например, что вы должны хранить ключи в полях класс , а не Cipher сам экземпляр . Поскольку ключи являются неизменяемыми, это означает, что состояние класса остается действительным, что бы ни происходило с Cipher экземплярами.


О коде / криптографии:

kpg.initialize(1024);

То есть не считается достаточно хорошим размером ключа больше. Попробуйте 3072 или выше. Или go для криптографии Ellipti c Curve (E CC).

encrypter = Cipher.getInstance("RSA");

никогда не забывайте указывать алгоритм full и не использовать значения по умолчанию. Выше указано использование "RSA/ECB/PKCS1Padding", но OAEP должен быть предпочтительным (но имеет еще больше накладных расходов).


String.getBytes() и new String(byte[]): всегда указывайте набор символов из StandardCharsets, если вы не связаны с кодировка платформы по умолчанию (и последующие ошибки декодирования на другой платформе).

...