Как расшифровать зашифрованный объект AES-256/CBC/ZeroBytePadding

Я новичок в криптографии Java. Я предоставил следующий PHP-код для расшифровки зашифрованного объекта AES-256/CBC/ZeroBytePadding.

function decrypt($key, $data)
{
    if (!in_array(strlen($key), array(32, 48, 64)))
{
    throw new Exception("Invalid key");
}

$key_hex = pack('H*', $key);

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);

$ciphertext = base64_decode($data);

# retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_dec = substr($ciphertext, 0, $iv_size);

# retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext, $iv_size);

# may remove 00h valued characters from end of plain text
$result = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key_hex, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

return $result;
}

Мне нужно сделать то же самое в java. После долгих поисков я сделал следующий код:

 public String decrypt(byte key[], String encrypted)
            throws GeneralSecurityException {
        if (key.length != 32 || key.length != 48 || key.length != 64) {
            throw new IllegalArgumentException("Invalid key size.");
        }
    byte[] ciphertextBytes = Base64.decodeBase64(encrypted.getBytes());

    // Need to find the IV length here. I am using 16 here

    IvParameterSpec iv = new IvParameterSpec(ciphertextBytes, 0, 16);
    ciphertextBytes = Arrays.copyOfRange(ciphertextBytes, 16,
            ciphertextBytes.length);

    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
    byte[] original = cipher.doFinal(ciphertextBytes);

    // remove zero bytes at the end
    int lastLength = original.length;
    for (int i = original.length - 1; i > original.length - 16; i--) {
        if (original[i] == (byte) 0) {
            lastLength--;
        } else {
            break;
        }
    }

    return new String(original, 0, lastLength);
}

Но мне нужно найти длину IV здесь. В PHP это делается с помощью: $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); Как это реализовать в java? Кто-нибудь может мне помочь?

Я вызываю метод следующим образом:

public static void main(String args[]) {
        String key =     "F5D4471791B79B6360C1EFF4A76250C1D7E5C23F5E4C3C43893B6CCAA796E307";
        String encrypted =     "F4N8SvpF1zgyMnQKwLlX\\/Dfgsj4kU58pg3kaSrt+AJt9D7\\/3vAfngegtytAdCUwwkQ2nxj8PVABRy0aaeBfsJN9n2Ltco6oPjdcmx8eOI";

    try {
        String decrypted = decrypt(Hex.decodeHex(key.toCharArray()), encrypted);
        System.out.println(decrypted);
    } catch (GeneralSecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

person Sumit    schedule 16.05.2016    source источник
comment
Пожалуйста, не редактируйте свой вопрос, чтобы включить ответ. Вы можете опубликовать ответ на свой вопрос. После того, как вы это сделаете, откатите свой вопрос до версии 4.   -  person Artjom B.    schedule 17.05.2016
comment
В ответе на ваш вопрос используется ключ, который не является допустимым шестнадцатеричным кодированием, поскольку он содержит символы за пределами 0-9, a-f, AF. Этот код должен выдавать ошибку.   -  person Artjom B.    schedule 17.05.2016


Ответы (2)


IV для AES (Rijndael-128) в режиме CBC всегда имеет тот же размер, что и блок, то есть 16 байт или 128 бит. Если вы продолжаете использовать режим CBC, вы можете жестко закодировать это значение или использовать Cipher#getBlockSize().

Если вы хотите использовать AES-256, вам необходимо установить файлы политики Unlimited Strength для JRE/JDK (Ссылка для Java 8). AES-128 можно использовать без изменений, но экспортные ограничения США требуют, чтобы вы сами включили более высокие размеры ключа.

person Artjom B.    schedule 16.05.2016
comment
Спасибо за вашу помощь. Теперь выдает ошибку: Неверная длина ключа AES: 64 байта. - person Sumit; 16.05.2016
comment
Я добавил код. Теперь ты можешь мне помочь? Я считаю, что string.getByte() является источником всех проблем. - person Sumit; 16.05.2016
comment
AES работает только с 16-, 24- и 32-байтными ключами. Кажется, у вас есть 64-символьный ключ, который будет 32-байтовым, если предполагается, что он закодирован в шестнадцатеричном формате. Если вы используете ту же строку ключей в PHP, вам нужно использовать только первые 32 символа. Если вы конвертируете фактический ключ в шестнадцатеричный, вам нужно преобразовать его обратно в Java. - person Artjom B.; 16.05.2016
comment
Я использую тот же ключ, который я упомянул там. Я не уверен, что вы имеете в виду, говоря, что если вы конвертируете фактический ключ в шестнадцатеричный, вам нужно преобразовать его обратно в Java. Не могли бы вы объяснить. В моем последнем редактировании я попытался закодировать ключ в HEX. Это дает недопустимый размер ключа. - person Sumit; 16.05.2016
comment
Вам необходимо установить файлы политики Unlimited Strength для вашей конкретной версии Java, если вы хотите использовать AES-256. - person Artjom B.; 16.05.2016
comment
В PHP-коде проверка размера выполняется перед декодированием ключа в байты, в Java — после этого. Таким образом, вы должны сравнить с 16, 24 или 32 байтами (т.е. 128, 192, 256 байт). Или, конечно, вы можете подождать, пока SecretKeySpec или Cipher не выдаст исключение. - person Maarten Bodewes; 16.05.2016
comment
Спасибо @Artjom B. за вашу помощь. Я нашел решение и разместил его в исходном вопросе. Не могли бы вы отредактировать и улучшить пункт 2 в ответе, чтобы помочь другим? - person Sumit; 17.05.2016

Ответ: С помощью Artjom B. я создал код, который будет расшифровывать зашифрованную строку AES-256 / CBC / ZeroBytePadding. Я публикую это для других, которым нужна помощь.

1. Во-первых, вам необходимо загрузить политику юрисдикции неограниченной силы Java Cryptography Extension (JCE) для вашей версии JDK.

2. Извлеките zip-файл и поместите файлы local_policy.jar и US_export_policy.jar в путь /JDK Path/jre/lib/security. Это необходимо, так как мой ключ 64 байта. AES требует 16/24/32 байт ключа.

3. Скопируйте и вставьте мой код и измените его в соответствии с вашими требованиями: P.

import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.binary.Base64;

public class Decryption {

public static void main(String args[]) {
//I have changed the original key. So mere copy pasting may not work. Put your key here.
    String key = "FfDaaaaaaa444aaaa7aaEFF4A76efaaaaaE5C23F5E4C3adeaaaaaaCAA796E307";
    String encrypted = "8AQ8SvpF1zgyNyxKwLlX\\/cGzwLE5skU58pg3kaSrt+AJt9D7\\/3vaNRPZISIKMdCUwwkQ2nxj8PVABRy0aaeBfsJN9n2Ltco6oPjdcmx8eOI";
    String decrypted = "";
    try {

        try {
            decrypted = decrypt(Hex.decodeHex(key.toCharArray()), encrypted);
        } catch (DecoderException e) {
            e.printStackTrace();
        }

        System.out.println(decrypted);
    } catch (GeneralSecurityException e) {
        e.printStackTrace();
    }

}

public static String decrypt(byte key[], String encrypted)
        throws GeneralSecurityException {
    /*
     * if (key.length != 32 || key.length != 48 || key.length != 64) { throw
     * new IllegalArgumentException("Invalid key size."); }
     */
    byte[] ciphertextBytes = Base64.decodeBase64(encrypted.getBytes());

    IvParameterSpec iv = new IvParameterSpec(ciphertextBytes, 0, 16);

    ciphertextBytes = Arrays.copyOfRange(ciphertextBytes, 16,
            ciphertextBytes.length);

    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
    byte[] original = cipher.doFinal(ciphertextBytes);

    // Remove zero bytes at the end.
    int lastLength = original.length;
    for (int i = original.length - 1; i > original.length - 16; i--) {
        if (original[i] == (byte) 0) {
            lastLength--;
        } else {
            break;
        }
    }

    return new String(original, 0, lastLength); 

}

}

Спасибо @Artjom B. за вашу помощь и опыт :).

person Sumit    schedule 17.05.2016