Данный последний блок не заполнен должным образом - PullRequest
98 голосов
/ 08 ноября 2011

Я пытаюсь реализовать алгоритм шифрования на основе пароля, но получаю следующее исключение:

javax.crypto.BadPaddingException: данный последний блок заполнен неправильно

Чтоможет быть проблема?(Я новичок в Java.)

Вот мой код:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(Тест JUnit)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}

Ответы [ 3 ]

180 голосов
/ 08 ноября 2011

Если вы попытаетесь расшифровать данные, заполненные PKCS5, с неверным ключом, а затем распаковать их (что автоматически выполняется классом Cipher), вы, скорее всего, получите исключение BadPaddingException (с вероятностью чуть меньше 255/256, около 99,61%), потому что заполнение имеет специальную структуру, которая проверяется во время распаковки, и очень немногие клавиши дают правильный заполнитель.

Итак, если вы получили это исключение, поймайте его и воспринимайте как «неправильный ключ».

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

Конечно, плохое заполнение также может произойти, если ваши данные повреждены при транспортировке.

Тем не менее, в вашей схеме есть несколько замечаний по безопасности:

  • Для шифрования на основе пароля вы должны использовать SecretKeyFactory и PBEKeySpec вместо использования SecureRandom с KeyGenerator. Причина в том, что SecureRandom может быть разным алгоритмом в каждой реализации Java, давая вам другой ключ. SecretKeyFactory выполняет получение ключа определенным образом (и способом, который считается безопасным, если вы выберете правильный алгоритм).

  • Не использовать режим ECB. Он шифрует каждый блок независимо, что означает, что идентичные блоки простого текста также всегда дают идентичные блоки зашифрованного текста.

    Предпочтительно использовать безопасный режим работы , такой как CBC (цепочка блоков шифрования) или CTR (счетчик). В качестве альтернативы используйте режим, который также включает аутентификацию, например GCM (режим счетчика Галуа) или CCM (счетчик с CBC-MAC), см. Следующий пункт.

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

  • Эффективный размер ключа DES составляет всего 56 бит. Это ключевое пространство довольно мало, оно может быть взломано за несколько часов преданным злоумышленником. Если вы сгенерируете свой ключ по паролю, это станет еще быстрее. Кроме того, DES имеет размер блока всего 64 бита, что добавляет еще несколько недостатков в режимах цепочки. Вместо этого используйте современный алгоритм, такой как AES, который имеет размер блока 128 бит и размер ключа 128 бит (для стандартного варианта).

1 голос
/ 08 ноября 2011

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

Конкретно в вашем случае вы выбрали схему заполнения PKCS5, которая описана здесь: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html

(я полагаю, у вас есть проблема при попытке шифрования)

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

Кстати, вы уверены, что хотите использовать симметричный механизм шифрования для шифрования паролей? Не было бы лучше односторонний хэш? Если вам действительно нужно иметь возможность расшифровывать пароли, DES - довольно слабое решение, вам может быть интересно использовать что-то более сильное, например AES, если вам нужно придерживаться симметричного алгоритма.

0 голосов
/ 10 декабря 2018

Я столкнулся с этой проблемой из-за операционной системы, простой для другой платформы о реализации JRE.

        new SecureRandom(key.getBytes())

получит одинаковое значение в Windows, в то время как в Linux оно будет другим.Поэтому в Linux нужно изменить на

        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(key.getBytes());
        kgen.init(128, secureRandom);

"SHA1PRNG" - используемый алгоритм, вы можете обратиться к здесь для получения дополнительной информации об алгоритмах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...