Как обрабатывать "последний блок, неполный в расшифровке"

У меня есть простой класс, чтобы попытаться обернуть шифрование для использования в другом месте в моей программе.

import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

public final class StupidSimpleEncrypter
{
    public static String encrypt(String key, String plaintext)
    {
        byte[] keyBytes = key.getBytes();
        byte[] plaintextBytes = plaintext.getBytes();
        byte[] ciphertextBytes = encrypt(keyBytes, plaintextBytes);
        return new String(ciphertextBytes);
    }

    public static byte[] encrypt(byte[] key, byte[] plaintext)
    {
        try
        {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(getRawKey(key), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, spec);
            return cipher.doFinal(plaintext);
        }
        catch(Exception e)
        {
            // some sort of problem, return null because we can't encrypt it.
            Utility.writeError(e);
            return null;
        }
    }

    public static String decrypt(String key, String ciphertext)
    {
        byte[] keyBytes = key.getBytes();
        byte[] ciphertextBytes = ciphertext.getBytes();
        byte[] plaintextBytes = decrypt(keyBytes, ciphertextBytes);
        return new String(plaintextBytes);
    }

    public static byte[] decrypt(byte[] key, byte[] ciphertext)
    {
        try
        {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(getRawKey(key), "AES");
            cipher.init(Cipher.DECRYPT_MODE, spec);
            return cipher.doFinal(ciphertext);
        }
        catch(Exception e)
        {
            // some sort of problem, return null because we can't encrypt it.
            Utility.writeError(e);
            return null;
        }
    }

    private static byte[] getRawKey(byte[] key)
    {
        try
        {
            KeyGenerator gen = KeyGenerator.getInstance("AES");
            SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
            rand.setSeed(key);
            gen.init(256, rand);
            return gen.generateKey().getEncoded();
        }
        catch(Exception e)
        {
            return null;
        }
    }
}

Кажется, что он правильно обрабатывает шифрование, но не так много, когда дешифрует, что бросает javax.crypto.IllegalBlockSizeException "последний блок, неполный в дешифровке" на выделенной строке. Вот трассировка стека:

Location:com.xxxxxx.android.StupidSimpleEncrypter.decrypt ln:49
last block incomplete in decryption
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
     at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:711)
     at javax.crypto.Cipher.doFinal(Cipher.java:1090)
     at com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:44)
     at com.xxxxxx.android.StupidSimpleEncrypter.decrypt(StupidSimpleEncrypter.java:34)

Я проделал большое количество ударов головой по столу, чтобы попытаться понять это, но если я вообще пойду, это станет другим исключением. Я также не могу найти много поиска.

Что мне не хватает? Я был бы признателен за любую помощь.

Ответ 1

Я не знаю, является ли это проблемой с IllegalBlockSizeException, но вы не должны кодировать ключ как String, особенно без указания кодировки символов. Если вы хотите сделать это, используйте что-то вроде Base-64, которое предназначено для кодирования любых "двоичных" данных, а не кодировки символов, которая отображает только определенные байты в символы.

Ключ, в общем, будет содержать байтовые значения, которые не соответствуют символу в кодировке платформы по умолчанию. В этом случае, когда вы создаете String, байт будет переведен на "заменяющий символ", U + FFFD (& # xFFFD;), и правильное значение будет безвозвратно потеряно.

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

Другая возможность заключается в том, что исходная платформа и кодировки символов целевой платформы различны, и что "декодирование" зашифрованного текста приводит к слишком малым байтам. Например, исходная кодировка является UTF-8 и интерпретирует два байта на входе как один символ, тогда как целевая кодировка - ISO-Latin-1, которая представляет этот символ как один байт.

Ответ 2

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

Ответ 3

Я отрывал свои волосы от этого, между ошибками "плохая основа 64" и "последний блок неполных"... Это, конечно, асимметрично. Вот суть, как я это сделал, которая, надеюсь, добавляет больше к обсуждению, чем если бы я попытался объяснить:

public String crypto(SecretKey key, String inString, boolean decrypt){
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    byte[] inputByte = inString.getBytes("UTF-8");
    if (decrypt){
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String (cipher.doFinal(Base64.decode(inputByte, Base64.DEFAULT)));
    } else {
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return new String (Base64.encode(cipher.doFinal(inputByte), Base64.DEFAULT));
    }
}

Ответ 4

Если вы работаете с массивом байтов, вы должны использовать тот же размер буфера. Например, есть bytearray, размер которого равен 1000. После шифрования этот размер становится 2000. (это не реальное значение). Если вы используете буфер для чтения всего зашифрованного файла, тогда вы должны выбрать buffersize для 2000. Я решил такую ​​же проблему таким образом.

Ответ 5

Для меня, я замечаю эту проблему, когда данные, подлежащие расшифровке, повреждены (отсутствует 1 символ). Возможно, это было связано с передачей данных через WiFi.