Шифрование AES в Java для соответствия с выводом C #

Я пытаюсь выполнить шифрование AES с использованием JAVA, я сделал несколько попыток, перепробовал много кодов и сделал много изменений, чтобы наконец добраться до места, где мой зашифрованный текст совпадает с зашифрованным текстом, созданным с использованием кода C #, НО ЧАСТИЧНО. Последний блок из 32 бит отличается. У меня нет доступа к коду C #, поскольку это сторонняя служба. Может ли кто-нибудь указать, что мне не хватает?

Упомянутые условия предназначены для использования:

Используйте 256-битное шифрование AES в режиме CBC и с заполнением PKCS5, чтобы зашифровать всю строку запроса с использованием вашего первичного ключа и вектора инициализации. (Не включайте дайджест сообщения в строку запроса.) Первичный ключ - это 64-значная шестнадцатеричная строка, а вектор инициализации - 32-значная шестнадцатеричная строка.

Я использовал следующие примерные значения:

Aes_IV = 50B666AADBAEDC14C3401E82CD6696D4

Aes_Key = D4612601EDAF9B0852FC0641DC2F273E0F2B9D6E85EBF3833764BF80E09DD89F (мой KeyMaterial)

Plain_Text = ss = brock & pw = 123456 & ts = 20190304234431 (ввод)

Зашифрованный_текст = 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A072AF44AADB62FA66F047EACA5C6A018 (вывод)

Мой вывод = 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A38E71E5C846BAA6C31F996AB05AFD089

public static String encrypt( String keyMaterial, String unencryptedString, String ivString ) {
    String encryptedString = "";
    Cipher cipher;
    try {
        byte[] secretKey = hexStrToByteArray( keyMaterial );
        SecretKey key = new SecretKeySpec( secretKey, "AES" );
        cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
        IvParameterSpec iv;
        iv = new IvParameterSpec( hexStrToByteArray( ivString ) );
        cipher.init( Cipher.ENCRYPT_MODE, key, iv );
        byte[] plainText = unencryptedString.getBytes( "UTF-8") ;
        byte[] encryptedText = cipher.doFinal( plainText );
        encryptedString = URLEncoder.encode(byteArrayToHexString( encryptedText ),"UTF-8");
    }
    catch( InvalidKeyException | InvalidAlgorithmParameterException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e ) {
        System.out.println( "Exception=" +e.toString() );
    }
    return encryptedString;
}

Я использовал это для конверсий.

public static byte[] hexStrToByteArray ( String input) {
    if (input == null) return null;
    if (input.length() == 0) return new byte[0];

    if ((input.length() % 2) != 0)
        input = input + "0";

    byte[] result = new byte[input.length() / 2];
    for (int i = 0; i < result.length; i++) {
        String byteStr = input.substring(2*i, 2*i+2);
        result[i] = (byte) Integer.parseInt("0" + byteStr, 16);
    }
    return result;
}
public static String byteArrayToHexString(byte[] ba) {
    String build = "";
    for (int i = 0; i < ba.length; i++) {
        build += bytesToHexString(ba[i]);
    }
    return build;
}
    public static String bytesToHexString ( byte bt) {
    String hexStr ="0123456789ABCDEF";
    char ch[] = new char[2];
    int value = (int) bt;

    ch[0] = hexStr.charAt((value >> 4) & 0x000F);
    ch[1] = hexStr.charAt(value & 0x000F);

    String str = new String(ch);

    return str;
}

Любые предложения, что мне делать, чтобы соответствовать выходам?


person Knight Rider    schedule 06.03.2019    source источник
comment
Когда я пропускаю ваши входные данные через реализацию AES в C #, я получаю тот же результат, что и вы (оканчивающийся на ... 089). Поскольку только последний блок (т.е. 16 байт) вызывает проблемы, я подозреваю, что служба C # использует другое заполнение.   -  person RogerN    schedule 06.03.2019
comment
Эталонное значение 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0A072AF44AADB62FA66F047EACA5C6A018 воспроизводимо, если сообщение (36 байт) дополнено значениями 0 до 48 (= 16 * 3) байтов. Однако это не PKCS5Padding, как уже подозревал @RogerN. Таким образом, необходимо проверить заполнение, используемое на стороне C #. Чистое заполнение нулевым байтом не совсем надежно (см., Например, crypto-it.net/ eng / theory / padding.html).   -  person user 9014097    schedule 06.03.2019


Ответы (1)


Если отличается только последний блок заполнения ECB / CBC, то вы можете быть уверены, что используется другое заполнение блочного шифра. Чтобы проверить, какое заполнение используется, вы можете попробовать (как это сделал Topaco в комментариях под вопросом) или расшифровать зашифрованный текст без заполнения. Для Java это было бы "AES/CBC/NoPadding".

Итак, если вы сделаете это с помощью ключа (и IV), вы получите следующий результат в шестнадцатеричном формате:

73733D62726F636B2670773D3132333435362674733D3230313930333034323334343331000000000000000000000000

Ясно, что это нулевое заполнение.

Нулевое заполнение имеет один большой недостаток: если ваш зашифрованный текст заканчивается нулевым байтом, этот байт можно рассматривать как заполнение и вычеркивать из результата. Обычно это не проблема для открытого текста, состоящего из строки ASCII или UTF-8, но может быть сложнее для двоичного вывода. Конечно, здесь мы предполагаем, что строка не использует нулевой терминатор, который, как ожидается, будет присутствовать в зашифрованном открытом тексте.

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

  1. заполнение всегда применяется и должно быть удалено, что означает, что если размер открытого текста ровно в несколько раз превышает размер блока, добавляется полный блок заполнения (так что для AES у вас будет 1..16 с нулевым значением байты в качестве заполнения);

  2. заполнение применяется только в случае строгой необходимости, что означает, что заполнение не применяется, если размер открытого текста ровно в несколько раз превышает размер блока (поэтому для AES у вас будет 0..15 байтов с нулевым значением в качестве заполнения).

Итак, в настоящее время для шифрования вам, возможно, придется проверить, какой из них ожидается / принят. Например. Bouncy Castle - который доступен для C # и Java - всегда (не) блокирует, в то время как ужасная библиотека PHP / mcrypt блокирует только там, где это необходимо.

Конечно, вы всегда можете выполнить собственное заполнение, а затем использовать "NoPadding" для Java. Однако помните, что вы никогда не распаковываете более 16 байт.

Общее предупреждение: шифрование без аутентификации неприменимо для безопасности транспортного режима.

person Maarten Bodewes    schedule 08.03.2019