Шифрование Java AES с CBC и PKCS7Padding

Я уже пару дней борюсь с этим. Мне необходимо использовать API, который принимает зашифрованный параметр. API был написан на C#. Запрошенное шифрование следующее:


Алгоритм: AES
Режим шифрования: CBC
Режим заполнения: PKCS7
Размер блока: 128
Размер ключа: 256

Ключ: String -> Ключ создается путем преобразования предоставленной строки в массив байтов размером 32: Encoding.ASCII.GetBytes (…). API заявляет, что строка создается ими с использованием хеш-функции строки MD5.

Массив IV: IV создается путем преобразования предоставленной строки в массив байтов размером 16: Encoding.ASCII.GetBytes (…).

Представление зашифрованной строки: Base64


После поиска и опробования стольких вещей, которые были предложены в Интернете, я все еще не могу получить такое же зашифрованное значение (особенно, что PKCS7 не поддерживается по умолчанию, а PKCS5 должен работать так же, но это не так). Вот некоторые вещи, которые я пробовал:
1) Использование bouncy castle jar для использования PKCS7
2) Добавление соответствия JCE, чтобы можно было снять ограничение на размеры ключей и блоков.

После того, как они связались с ними, они отправили мне работающий фрагмент Android (который, если я запустил простой java 8, жалуется на поставщика (NoSuchAlgorithmException: не удается найти ни одного поставщика, поддерживающего AES / CBC / PKCS7Padding)):

public static String encrypt(String value) {
        String plainText = value;
        String escapedString;
        try {
            byte[] key = ENCRYPT_KEY.getBytes("UTF-8");
            byte[] ivs = ENCRYPT_IV.getBytes("UTF-8");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivs);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
            escapedString = Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")), Base64.DEFAULT).trim();

            return escapedString;
        } catch (Exception e) {
            e.printStackTrace();
            return value;
        }
    }  

Пожалуйста, любая помощь будет очень признательна.

Вот фрагмент кода из того, что я пробовал:

package com.melhem.TestJava;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class StringFunc {


    final static String key = "API_KEY_32_CHARs";
    final static String iv = "API_IV_16_CHARs";
    final static String algorithm = "AES/CBC/PKCS7Padding";
    private static Cipher cipher = null;
    private static SecretKeySpec skeySpec = null;
    private static IvParameterSpec  ivSpec = null;

    public static void main(String[] args) {
        System.out.println(encrypt("STRING_TO_ENCODE"));
    }

    private static void setUp(){
        try{
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
            skeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
            ivSpec = new IvParameterSpec(iv.getBytes("ASCII"));
            cipher = Cipher.getInstance(algorithm);
        }catch(NoSuchAlgorithmException | NoSuchPaddingException ex){

            ex.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static String encrypt(String str){
        try{
//            Integer strL = (int) Math.ceil(str.length() / 8.0);
//            Integer strB = strL*8;
//            str = padRight(str, ' ', strB);
            setUp();
            try {
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
                System.out.println("Block size: " + cipher.getBlockSize() * 8);
                System.out.println("Algorithm name: " + cipher.getAlgorithm());
                System.out.println("Key size: " + skeySpec.getEncoded().length * 8);
            } catch (InvalidAlgorithmParameterException ex) {
                ex.printStackTrace();
                return "";
            }
            byte[] enc = cipher.doFinal(str.getBytes("ASCII"));
            String s = new String(Base64.getEncoder().encode(enc));
            s = s.replace("+", "__plus__");
            s = s.replace("/", "__slash__");
            return s;
        }catch(InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex){
            ex.printStackTrace();
            return "";            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return "";
        }
    }

    public static String padRight(String msg, char x, int l) {
        String result = "";
        if (!msg.isEmpty()) {
            for (int i=0; i<(l-msg.length()); i++) {
                result = result + x;
            }
            result = msg + result;
        }
        return result;
    }
}

Вот чего не хватает в вашем вопросе: кода Java, который, как вы утверждаете, вы пробовали, но не сработали.

President James K. Polk 04.11.2018 14:26

Что ж, этот код не приведет к исключению, которое, как вы утверждаете, получают в другом месте. Пожалуйста, прочтите Как создать минимальный, полный и проверяемый пример. MCVE - это способ решить ваши проблемы с помощью stackoverflow.

President James K. Polk 04.11.2018 17:09

Ваша проблема не в заполнении. В Java заполнение PKCS7 называется PKCS5 (обратите внимание, что AES с заполнением PKCS5 невозможно). Проблема, скорее всего, связана с "хешированием MD5" и кодировкой строк. Если вы предоставите образец ввода и ожидаемый результат, возможно, мы сможем перепроектировать его для вас. Да, и укажите свой точный номер версии Java (например, 1.8.161).

rustyx 04.11.2018 21:02
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
3
7 021
1

Ответы 1

Java Пакет шифров поддерживает только заполнение PKCS # 7 с помощью AES/CBC/PKCS5Padding. Это неправильное именование, поскольку заполнение PKCS # 5 поддерживает размеры блоков 8 байтов, поскольку DES и PKCS # 7 поддерживают до 255 байтов. Для Java используйте это;

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

# 5 и # 7 не взаимозаменяемы для большинства современных блочных шифров, поскольку AES - это 128-битный блочный шифр. См. Вопрос по Crypto.StackExchange.

и, для использования AES с размером ключа 256 бит;

Стандартная библиотека шифров Java ограничена размером ключа 128 бит. Вы должны пойти и скачать Java Файлы политики юрисдикции с неограниченной надежностью Cryptography Extension (JCE) 6

Спасибо, kelalaka .. Я пробовал, но он выдает исключение InvalidAlgorithmParameterException: Неверная длина IV: должна быть 8 байтов .. Кроме того, API запрашивает размер IV 16 байтов, а алгоритм - AES

M.R.M 04.11.2018 10:37

Да, я пробовал использовать это раньше, так как они взаимозаменяемы, но это дает другое значение, которое API не может расшифровать. Как вы думаете, это означает, что владелец API выполняет дополнительные действия после шифрования, или проблема заключается в использовании PKCS5Padding?

M.R.M 04.11.2018 10:42

Я уже добавил обе банки из JCE, но проблема все еще остается .. Дело не в том, что он жалуется на размер ключа после добавления JCE-банок, но дает неправильное значение

M.R.M 04.11.2018 10:50

ты забыл сделать финал?

kelalaka 04.11.2018 10:58

Если вы используете последнюю версию Java 8 или выше, вам больше не нужны файлы политики юрисдикции с неограниченной надежностью, они уже включены.

Robert 04.11.2018 13:45

Обратите внимание, что Oracle 8u161 и более поздние версии (с конца 2017 года) больше не нуждаются в загрузке «неограниченной политики» - OOTB позволяет использовать 256-битную симметричность. @ M.R.M: Политика FYI никогда не ограничивает размер блока (только ключ). «IV должен быть 8 байтов» для AES / CBC - безумие. И это плохая практика, поэтому подозрительно рассматривать ключ или IV как символы - а также очень плохая практика, чтобы IV был известен заранее. или повторяется, как указывает Саптарши. Есть ли у них какие-либо тестовые примеры или тестовая среда, о которых вы могли бы подробно рассказать, не вызывая проблем с безопасностью?

dave_thompson_085 04.11.2018 14:28

@ dave_thompson_085 К сожалению, у них нет ... и я согласен, но они реализовали это именно так, но они сказали, что мы должны приспособиться, и они не могут изменить API, поскольку он используется другими и работает (хотя я сказал им их путь небезопасно) .. но что касается IV, это 16 байт, а не 8

M.R.M 04.11.2018 16:15

Другие вопросы по теме