AES BadPaddingException

если я использую неправильный ключ или неправильную соль для расшифровки, выдается исключение BadPaddingException. Я ожидаю, что будет возвращена неправильная строка. doFinal() вызывает исключение в методе расшифровки

Сообщение : This is just an example

Без запаха : 'ΩÙΩ„SåF?V®ßs.k˚·ºç€èÀHfif∫ÙÉÕ

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
    at javax.crypto.Cipher.doFinal(DashoA13*..)
    at casino.AES.decryptString(AES.java:130)
    at casino.AES.main(AES.java:172)

     public static void main(String[] args) throws Exception {
        //Encryption
        AES encr = new AES();   
        encr.setKey("KEY");
        encr.setSalt("SALT");
        encr.setup();
        String message = "This is just an example";
        System.out.println("Message : " + message);



        byte[] code = encr.encrypt(message);
        System.out.println("Encrypted Strinng : "+ new String(code, "UTF-8"));

        //Decryption
        AES dec = new AES();
        dec.setKey("INCORRECT"); //<--- incorrect 
        dec.setSalt("SALT");
        dec.setup();

        System.out.println(dec.decryptString(code));
    }




        public synchronized  void setKey(String key) throws UnsupportedEncodingException {
        this.key = key.getBytes("UTF-8");
        isPasswordAlreadySet = true;
    }


    public synchronized  void setSalt(String salt) throws UnsupportedEncodingException {
        this.salt = salt.getBytes("UTF-8");
    }

    public synchronized  void setup() throws Exception {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    digest.update(key);
    digest.update(salt);
    byte[] raw = digest.digest();

    skeySpec = new SecretKeySpec(raw, "AES");
    cipher = Cipher.getInstance("AES");
    }  

public synchronized byte[] encrypt(byte[] klartext) throws Exception {
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

    byte[] encrypted = cipher.doFinal(klartext);

    return encrypted;
    }

    public synchronized byte[] encrypt(String klartext) throws Exception{
    return encrypt(klartext.getBytes("UTF-8")); 
    }






     public synchronized byte[] decrypt(byte[] code) throws Exception {
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(code);
    return original;
    }

    public synchronized double decryptDouble(byte[] code) throws Exception {
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(code);
    return doubleFromBytes( original);
    }

Благодарю вас! Фредерик


person Frederik    schedule 08.04.2011    source источник


Ответы (3)


Набивка — это хорошая проверка на вменяемость. Предполагая, что неправильно расшифрованные данные распределены равномерно, они будут выглядеть как правильно дополненные PKCS5/PKCS7 примерно 1 раз на каждые 255 неправильных паролей. (1/256 + 1/256^2 + 1/256^3...)

Так что это полезно, но вам не следует полагаться на это — то, что фактически является почти 8-битным дайджестом сообщения, не является достаточным тестом целостности данных.

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

Кстати, если вы действительно хочете вести себя так, как ожидали, вы можете использовать "AES/CTR/NoPadding", который не требует точного размера блока и всегда будет возвращать расшифрованный байт[], будь то или ключи не совпадают.

person maybeWeCouldStealAVan    schedule 28.04.2012

Вы должны либо использовать AES с неявным объявлением заполнения (см. доступные режимы) или задайте длину (в байтах) зашифрованных/расшифрованных данных, кратную 16.

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

person Léo Germond    schedule 08.04.2011
comment
Я изменил строку инициализации шифра на: cipher = Cipher.getInstance(AES/CBC/PKCS5Padding); Но выбрасывается такое же исключение.. Для получения правильной длины я использовал SHA-Hash - думаю этого должно хватить. Более того, в первый раз, когда я использую метод setup(), исключение не выдается. - person Frederik; 08.04.2011
comment
@Frederik: Вы должны понимать, как работает заполнение: режим заполнения добавит некоторые данные к обычным текстовым данным перед шифрованием. После расшифровки это дополнение снова удаляется. Но чтобы знать, сколько байтов нужно удалить, важно содержимое заполнения, и если мы расшифровали с неправильным ключом, последние несколько байтов не будут в одном из форматов, которые будут добавлены алгоритмом заполнения. Как сказано, используйте NoPadding, но тогда вам нужно будет убедиться, что длина ваших данных кратна длине блока. - person Paŭlo Ebermann; 08.04.2011
comment
Для PKCS5Padding это то же самое, что описано в RFC 1423 для ДЕС ЦБК. - person Paŭlo Ebermann; 08.04.2011
comment
Извините, я все еще не понимаю проблему. Мои зашифрованные данные имеют длину 32. Длина блока 16, не так ли? Является ли это пробелом в безопасности, если я продолжу и позволю шифру генерировать исключение, если длина недействительна? - person Frederik; 08.04.2011

SecretKeySpec указывает секретный ключ независимым от провайдера способом.

http://themasterofmagik.wordpress.com/2014/03/19/simple-aes-encryption-and-decryption-in-java/

person Nik theGeeK    schedule 19.03.2014