javax.crypto.BadPaddingException: данный последний блок не заполнен должным образом - Странная ошибка - PullRequest
0 голосов
/ 08 февраля 2019

javax.crypto.BadPaddingException: данный последний блок не заполнен должным образом - это ошибка, которую я получил.Я не знаю почему.Мой код, кажется, в порядке.Ключ одинаков во время шифрования и дешифрования.Что я могу сказать, так это то, что ни один из уже отвеченных вопросов не содержит решения моей проблемы.Вот мой код:

public String decrypt(String text) throws Exception {

    String key = "SiadajerSiadajer"; // 128 bit key
    // Create key and cipher
    Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    // encrypt the text
    byte[]encrypted = text.getBytes();
    cipher.init(Cipher.DECRYPT_MODE, aesKey);
    String decrypted = new String(cipher.doFinal(encrypted)); //Here is the error

    return decrypted;


}

И механизм шифрования в php

function encrypt($data, $size)
{
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}

function decrypt($data)
{
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $iv);

EDIT

Теперь в части php мой код:

$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$EIV = base64_encode($iv);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $EIV);

И это дает мне предупреждение: Предупреждение: openssl_encrypt (): длина передаваемого IV составляет 24 байта, что больше, чем ожидаемый выбранный шифр, 16, сокращается в C: \ xampp2 \ htdocs \ standardfinalinserting.php в строке 68

А в части Java мой метод расшифровки такой же, как в ответе на мой вопрос, но после запуска он выдает мне ошибку: java.security.InvalidKeyException: недопустимый размер ключа в строке:

шифр.init (Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

РЕДАКТИРОВАТЬ 2:

Так что это весь мой основной класс.Он содержит весь пример кода Java

public class Main {

    private byte[] padKey(byte[] key) {
        byte[] paddedKey = new byte[32];
        System.arraycopy(key, 0, paddedKey, 0, key.length);
        return paddedKey;
    }

    private byte[] unpad(byte[] data) {     
        byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
        System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
        return unpaddedData;
    }

    public String decrypt(String encodedJoinedData) throws Exception {

        // Base64-decode the joined data
        byte[] joinedData = Base64.decode(encodedJoinedData); 

        // Get IV and encrypted data
        byte[] iv = new byte[16];
        System.arraycopy(joinedData, 0, iv, 0, iv.length);
        byte[] encryptedData = new byte[joinedData.length - iv.length];
        System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);

        // Pad key
        byte[] key = padKey("SiadajerSiadajer".getBytes()); 
        Key aesKey = new SecretKeySpec(key, "AES");

        // Specify CBC-mode
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 
        cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); //HERE IS THE ERROR

        // Decrypt data
        byte[] decryptedData = cipher.doFinal(encryptedData);

        // Remove custom padding
        byte[] unpaddedData = unpad(decryptedData);         

        return new String(unpaddedData);
    }

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
         String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
            String decryptedData = new Main().decrypt(encodedJoinedData);
            System.out.println(decryptedData + " - " + decryptedData.length());



     }



}

Запуск кода приводит к ошибке:

Exception in thread "main" java.security.InvalidKeyException: Illegal key size
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
    at javax.crypto.Cipher.implInit(Cipher.java:805)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at com.dd.escuel.Main.decrypt(Main.java:43)
    at com.dd.escuel.Main.main(Main.java:57)

1 Ответ

0 голосов
/ 09 февраля 2019

Есть несколько проблем с Java-кодом:

  1. В PHP-коде используется AES-256, таким образом, ключ должен иметь длину 32 байта.Более короткие клавиши автоматически дополняются нулями.Это происходит в вашем PHP-коде, поскольку длина ключа SiadajerSiadajer составляет всего 16 байт.Клавиши также должны быть выполнены в Java-коде.Для этого, например, можно использовать следующий Java-метод:

    private byte[] padKey(byte[] key) {
        byte[] paddedKey = new byte[32];
        System.arraycopy(key, 0, paddedKey, 0, key.length);
        return paddedKey;
    }
    
  2. При Cipher.getInstance("AES") ECB -режим и PKCS5Padding по умолчанию выбраны .CBC -мод должен быть явно указан в Java-коде с помощью

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    
  3. В PHP-методе openssl_encrypt четвертый параметр $options установлен на 0, что означает, что возвращаемые данные закодированы в Base64.Таким образом, в Java-коде данные должны быть декодированы Base64 перед расшифровкой:

    byte[]encryptedData = Base64.decode(text); 
    
  4. Поскольку используется CBC -режим, IV шифрования долженбыть принятым во внимание.Возможный подход заключается в кодировании Base64 IV в PHP-коде (после шифрования) с помощью

    $encodedIV = base64_encode($iv);
    

    и передаче этого значения в Java-метод decrypt в качестве второго параметра.Здесь IV должен быть декодирован и использован для расшифровки:

    byte[] iv = Base64.decode(ivEncoded);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 
    ...
    cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
    
  5. В PHP-методе openssl_encrypt четвертый параметр $options имеет значение 0Это означает, что используется заполнение по умолчанию (PKCS7).Более того, в PHP-методе encrypt реализован вид пользовательского заполнения (кстати: название метода не подходит, так как он не шифруется), и, следовательно, он дополняется дважды.Следовательно, после расшифровки пользовательский отступ (который может состоять из пробелов) должен быть удален в Java-коде:

    byte[] unpaddedData = unpad(decryptedData);
    

    с

    private byte[] unpad(byte[] data) {     
        byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
        System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
        return unpaddedData;
    }
    

Всего:

public String decrypt(String text, String ivEncoded) throws Exception {

    // Pad key
    byte[] key = padKey("SiadajerSiadajer".getBytes()); 
    Key aesKey = new SecretKeySpec(key, "AES");

    // Base64 decode data
    byte[]encryptedData = Base64.decode(text); 

    // Base64 decode iv
    byte[] iv = Base64.decode(ivEncoded);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 

    // Specify CBC-mode
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

    // Decrypt
    byte[] decryptedData = cipher.doFinal(encryptedData);

    // Remove custom padding
    byte[] unpaddedData = unpad(decryptedData);         

    return new String(unpaddedData);
}

Тест:

Ввод PHP-кода: обычный текст ($data), клавиша ($key):

$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";

Вывод PHP-кода: IV в кодировке Base64 ($encodedIV), зашифрованные данные ($name):

$encodedIV = 'Dg+Zs3mqIJeDOOEPMT5F4Q==';
$name = '8dXjeQhx2WSswQOQXLcyMKNpa5s413yI2Ku8WiIB/xtA2pEjrKcl5kWtrOh9k4A12Jl0N/z6tH67Wybhp/OwTi1NtiJOZxl3w6YQufE29oU=';

Если этот вывод используется в качестве ввода для метода Java decryptдешифрованные данные равны обычному тексту.

Что касается PHP-кода, я бы предложил удалить либо пользовательский, либо стандартный (PKCS7) отступ (если есть выбор).Последнее может быть достигнуто путем использования флага OPENSSL_ZERO_PADDING в качестве четвертого параметра в методе openssl_encrypt (примечание: этот флаг означает не «заполнение с нулевыми значениями», а «отсутствие заполнения»).Если пользовательский отступ сохраняется, вы должны как минимум переименовать PHP-методы encrypt и decrypt в pad и unpad (или что-то подобное), соответственно.

Как указано уже вкомментарии, режим GCM может быть лучшим выбором, чем режим CBC.Однако было бы полезно получить информацию об основах до кодирования, например, разница между CBC - и GCM -режимом , объяснение режима GCM здесь и здесь и ловушек вместе с режимом GCM (GCM безопасен, но только если вы следуете определенным рекомендациям, например uniqe IV / nonce для каждого сообщения, зашифрованного тем же ключом).

Вы можете использовать PHP-метод openssl_get_cipher_methods , чтобы узнать допустимые AES-режимы, поддерживаемые в PHP.Это объясняется более подробно здесь .Для AES-256 и AES-режима GCM необходимо указать aes-256-gcmстрочными (!) Буквами).Предположительно, поэтому вы получаете ошибку «Неизвестный алгоритм шифрования».

РЕДАКТИРОВАТЬ:

Вы можете использовать для шифрования следующий PHP-код (который являетсянемного измененная версия вашего PHP-кода из вопроса):

<?php
function pad($data, $size) {
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16; 
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key,  0, $iv);
print base64_encode($iv)."\n".$encryptedData."\n";

РЕДАКТИРОВАТЬ 2:

IV и зашифрованные данные могут быть объединены до или после Base64-кодировки.Первый более эффективен, и поэтому я реализовал этот вариант.Однако некоторые изменения необходимы в PHP- и Java-коде, и поэтому я публикую все методы, которые были изменены.

PHP-код становится:

<?php
function pad($data, $size) {
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16; 
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$joinedData = $iv.$encryptedData;
$encodedJoinedData = base64_encode($joinedData);
print $encodedJoinedData."\n";

И Jave-метод decrypt становится:

public String decrypt(String encodedJoinedData) throws Exception {

    // Base64-decode the joined data
    byte[] joinedData = Base64.decode(encodedJoinedData); 

    // Get IV and encrypted data
    byte[] iv = new byte[16];
    System.arraycopy(joinedData, 0, iv, 0, iv.length);
    byte[] encryptedData = new byte[joinedData.length - iv.length];
    System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);

    // Pad key
    byte[] key = padKey("SiadajerSiadajer".getBytes()); 
    Key aesKey = new SecretKeySpec(key, "AES");

    // Specify CBC-mode
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 
    cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

    // Decrypt data
    byte[] decryptedData = cipher.doFinal(encryptedData);

    // Remove custom padding
    byte[] unpaddedData = unpad(decryptedData);         

    return new String(unpaddedData);
}

и пример для Java-метода main:

public static void main(String[] args) throws Exception {
    String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
    String decryptedData = new Main().decrypt(encodedJoinedData);
    System.out.println(decryptedData + " - " + decryptedData.length());
}

Использование выглядит следующим образом:

  1. Шифрование вашего простого текста с помощью PHP-код.В приведенном выше примере (PHP-код) обычный текст имеет вид

    $data = 'This is a plain text which needs to be encrypted...';
    
  2. Затем передайте строку, содержащуюся в $ encodedJoinedData, в Java-метод decrypt.В приведенном выше примере (main -метод) строка имеет вид

    String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
    
  3. Java-метод decrypt предоставит исходный простой текст.

Последнее замечание : Если вы решили удалить (избыточный) пользовательский отступ, замените в PHP-коде строку

$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key,  OPENSSL_RAW_DATA, $iv);

с помощью

$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key,  OPENSSL_RAW_DATA, $iv);

и удалите pad - и unpad -метод.

В Java-коде замените строки

// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);         

return new String(unpaddedData);

на

return new String(decryptedData);

и удалите unpad -метод.

Edit 3:

InvalidKeyException (Illegal key size), упомянутый в разделе Edit2 разделавопрос уже обсуждался, например, здесь InvalidKeyException Недопустимый размер ключа и здесь Безопасность Java: недопустимый размер ключа или параметры по умолчанию? .

...