Невозможно воспроизвести метод шифрования с Java на PHP с помощью AES / ECB / PKCS5Padding

У меня есть следующий код Java

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class AESEncryption 
{
    public static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    public static final String AES_ALGORITHM = "AES";
    public static final int ENC_BITS = 256;
    public static final String CHARACTER_ENCODING = "UTF-8";

    private static Cipher ENCRYPT_CIPHER;
    private static Cipher DECRYPT_CIPHER;
    private static KeyGenerator KEYGEN; 

    static
    {
        try
        {
            ENCRYPT_CIPHER = Cipher.getInstance(AES_TRANSFORMATION);
            DECRYPT_CIPHER = Cipher.getInstance(AES_TRANSFORMATION);
            KEYGEN = KeyGenerator.getInstance(AES_ALGORITHM);
            KEYGEN.init(ENC_BITS);
        }
        catch(NoSuchAlgorithmException | NoSuchPaddingException e) 
        {
            e.printStackTrace();
        }
    }

    /**
     * This method is used to encode bytes[] to base64 string.
     * 
     * @param bytes
     *            : Bytes to encode
     * @return : Encoded Base64 String
     */

    private static String encodeBase64String(byte[] bytes) 
    {
         return new String(java.util.Base64.getEncoder().encode(bytes));
    }

    /**
    * This method is used to decode the base64 encoded string to byte[]
    * 
    * @param stringData
    *            : String to decode
    * @return : decoded String
    * @throws UnsupportedEncodingException
    */

    private static byte[] decodeBase64StringTOByte(String stringData) throws Exception 
    {
        return java.util.Base64.getDecoder().decode(stringData.getBytes(CHARACTER_ENCODING));
    }

    /**
    * This method is used to encrypt the string which is passed to it as byte[] and return base64 encoded
    * encrypted String
    * @param plainText
    *            : byte[]
    * @param secret
    *            : Key using for encrypt
    * @return : base64 encoded of encrypted string.
    * 
    */

    private static String encryptEK(byte[] plainText, byte[] secret)
    {
        try
        {
            SecretKeySpec sk = new SecretKeySpec(secret, AES_ALGORITHM);
            ENCRYPT_CIPHER.init(Cipher.ENCRYPT_MODE, sk);
            return Base64.encodeBase64String(ENCRYPT_CIPHER.doFinal(plainText));    
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return "";
        }
    }

    /**
    * This method is used to decrypt base64 encoded string using an AES 256 bit key.
    * 
    * @param plainText
    *            : plain text to decrypt
    * @param secret
    *            : key to decrypt
    * @return : Decrypted String
    * @throws IOException
    * @throws InvalidKeyException
    * @throws BadPaddingException
    * @throws IllegalBlockSizeException
    */
    public static byte[] decrypt(String plainText, byte[] secret)
                throws InvalidKeyException, IOException, IllegalBlockSizeException,
                BadPaddingException,Exception 
    {
        SecretKeySpec sk = new SecretKeySpec(secret, AES_ALGORITHM);
        DECRYPT_CIPHER.init(Cipher.DECRYPT_MODE, sk);       
        return DECRYPT_CIPHER.doFinal(Base64.decodeBase64(plainText));
    }

    public static void main(String args[])throws Exception
    {
        String encKey = ""; 

        //client asp_secret
        String asp_secret = "";   


        byte[] enc_key = decrypt(encKey, asp_secret.getBytes());

        String enc_asp_secret=encryptEK(asp_secret.getBytes(), decodeBase64StringTOByte(encodeBase64String(enc_key)));

        System.out.println("asp secret encrypted:");
        System.out.println(enc_asp_secret);     
    }
}

Мне довелось увидеть очень похожий пост в StackOverflow без ответов

Невозможно скопировать код шифрования AES 256 с Java на PHP [дубликат]

который помечен как дубликат другого вопроса, который отличается.

Я пробовал пару кодов PHP, но ничего не вышло.

Я добавлю за это награду, поскольку пытаюсь это сделать целую вечность.

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

Добавление кода PHP, который я пробовал

class AtomAES {

public function encrypt($data = '', $key = NULL, $salt = "") {
    if ($key != NULL && $data != "" && $salt != ""){

        $method = "AES-256-CBC";

        //Converting Array to bytes
        $iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
        $chars = array_map("chr", $iv);
        $IVbytes = join($chars);


        $salt1 = mb_convert_encoding($salt, "UTF-8"); //Encoding to UTF-8
        $key1 = mb_convert_encoding($key, "UTF-8"); //Encoding to UTF-8

        //SecretKeyFactory Instance of PBKDF2WithHmacSHA1 Java Equivalent
        $hash = openssl_pbkdf2($key1,$salt1,'256','65536', 'sha1'); 

        $encrypted = openssl_encrypt($data, $method, $hash, OPENSSL_RAW_DATA, $IVbytes);

        return bin2hex($encrypted);
    }else{
        return "String to encrypt, Salt and Key is required.";
    }
}

public function decrypt($data = "", $key = NULL, $salt = "") {
    if ($key != NULL && $data != "" && $salt != ""){
        $dataEncypted = hex2bin($data);
        $method = "AES-256-CBC";

        //Converting Array to bytes
        $iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
        $chars = array_map("chr", $iv);
        $IVbytes = join($chars);

        $salt1 = mb_convert_encoding($salt, "UTF-8");//Encoding to UTF-8
        $key1 = mb_convert_encoding($key, "UTF-8");//Encoding to UTF-8

        //SecretKeyFactory Instance of PBKDF2WithHmacSHA1 Java Equivalent
        $hash = openssl_pbkdf2($key1,$salt1,'256','65536', 'sha1'); 

        $decrypted = openssl_decrypt($dataEncypted, $method, $hash, OPENSSL_RAW_DATA, $IVbytes);
        return $decrypted;
    }else{

        return "Encrypted String to decrypt, Salt and Key is required.";

    }
}

}

Я не могу расшифровать строку, сгенерированную с помощью java, с помощью этого PHP

Обновлять

вот текст и ключ, которые я пробовал для шифрования с использованием вышеуказанного java-кода

Random generated text (asp_secret)  : DTosv9G179D0cY1985Uh2eF6ND80C95L
Random generated Key used (encKey): VEMwcCYfFpsrXQVIFTDrA/2zP/5PYOY6JC1XEkEcLGSk/klt+HqHzGSr781Yznku
Encrypted string using above java code (enc_asp_secret): zAnTcjmAezfdzrWGixyfwmb8cM0otrsmwJ8+cNDs48Axh9hYgBtCJyeSE9tCvEBz

Да, я пробовал решение по ссылке. Вот почему я не разместил этот вопрос.

Ajeesh 13.12.2018 17:06

Подождите, босс. Позвольте мне добавить это.

Ajeesh 13.12.2018 17:09

Почему я должен голосовать против тебя. Я ищу ответы.

Ajeesh 13.12.2018 17:53

Может быть. Без проблем

Ajeesh 13.12.2018 17:59

Я улучшил свой вопрос на основе вашего предложения

Ajeesh 13.12.2018 18:00

Если мне чего-то не хватает (что вполне может быть), ваш Java-код использует AES-256-ECB (на основе «AES / ECB / PKCS5Padding»), но ваш PHP использует AES-256-CBC.

Chris Forrence 13.12.2018 18:05

@ChrisForrence Да, я знаю. Когда я попытался использовать AES / ECB / PKCS5Padding в PHP, он не работал. Я изо всех сил пытаюсь найти альтернативу AES / ECB / PKCS5Padding, эквивалентную в PHP

Ajeesh 13.12.2018 18:06

@kelalaka На этот вопрос отвечает Topaco. Вы можете обратиться к этому ответу в будущем, если вам понадобится

Ajeesh 21.12.2018 09:10
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
9
1 955
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку вас интересует расшифровка зашифрованной строки, в которой шифрование было выполнено с помощью метода Java encryptEK, а расшифровка должна выполняться с помощью метода PHP decrypt (или наоборот), я игнорирую код метода main ( что мне не очень понятно), и я сосредоточусь на портировании обоих Java-методов, encryptEK и decrypt, на PHP-методы.

Метод Java encryptEK принимает простой текст и ключ в виде массива байтов, шифрует простой текст с помощью AES (256-ECB) и кодирует зашифрованный текст с использованием кодировки Base64. Возможный аналог PHP:

public function encrypt($data = '', $key = NULL) {
    if ($key != NULL && $data != ""){
        $method = "AES-256-ECB";
        $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA);
        $result = base64_encode($encrypted);
        return $result;
    }else{
        return "String to encrypt, Key is required.";
    }
}

Примечание. В режиме ECB не используется IV.

Метод Java decrypt берет строку в кодировке base64, декодирует ее, а затем расшифровывает. Возможным аналогом PHP является

public function decrypt($data = "", $key = NULL) {
    if ($key != NULL && $data != ""){
        $method = "AES-256-ECB";
        $dataDecoded = base64_decode($data);
        $decrypted = openssl_decrypt($dataDecoded, $method, $key, OPENSSL_RAW_DATA);
        return $decrypted;
    }else{
        return "Encrypted String to decrypt, Key is required.";
    }
}

Оба Java-метода, encodeBase64String и decodeBase64StringTOByte, которые используют класс java.util.Base64, не используются Java-методами encryptEK и decrypt. Вместо этого используются соответствующие методы класса org.apache.commons.codec.binary.Base64 (например, https://commons.apache.org/proper/commons-codec/download_codec.cgi). По этой причине я больше не обращаю внимания на оба метода.

В ссылочном коде Java 256-битный ключ AES не генерируется, но случайный ключ обычно генерируется следующим образом:

KEYGEN.init(256);
SecretKey secretKey = KEYGEN.generateKey();
byte[] key = secretKey.getEncoded();

В PHP это делается с помощью

$key = random_bytes(32);

Для тестирования смешанного шифрования / дешифрования (например, Java / PHP) с обеих сторон должен использоваться один и тот же ключ. Например, этот ключ предоставляется в Java:

byte[] key = "This is a 256 bits = 32 byte key".getBytes(Charsets.UTF_8);

и в PHP:

$key = mb_convert_encoding("This is a 256 bits = 32 byte key", "UTF-8");

Тест 1: шифрование / дешифрование с помощью Java (с использованием случайно сгенерированного ключа)

Plain text: The quick brown fox jumps over the lazy dog
Randomly generated key (hex): 20e9c191374b688e74e68ab6c969109e84c5c8e059d84f16f2beb07a7545cbc8
Encrypted text (base64 encoded): ZWOnSYErRxRRtqoVFTLVQMT329pOFHzN1gPDMuiZt0zFpt4n2TF/L54RB21zhVUa
Decrypted text: The quick brown fox jumps over the lazy dog

Тест 2: шифрование / дешифрование с помощью PHP (с использованием случайно сгенерированного ключа)

Plain text: The quick brown fox jumps over the lazy dog
Randomly generated key (hex): eecd40c21e2a395f3aa3baeac19bfc8dcee04ea6e07f02dca7069397a487824f
Encrypted text (base64 encoded): 8wjusOED9TTXHjyEqvmGExLATVlvhg3hXEBHQ6Ku3Fos2OrYKbF+4XdO6cD9JJA5
Decrypted text: The quick brown fox jumps over the lazy dog

Возможная часть шифрования и дешифрования:

$key = random_bytes(32);
echo bin2hex($key);
$atomAES = new AtomAES();
$encrypt = $atomAES->encrypt("The quick brown fox jumps over the lazy dog", $key);
echo $encrypt; 
$decrypt = $atomAES->decrypt($encrypt, $key);
echo $decrypt;

Тест 3: шифрование с помощью Java / дешифрование с помощью PHP (с использованием конкретного ключа выше)

Plain text: The quick brown fox jumps over the lazy dog
Encrypted text (base64 encoded) with Java: /XjXJc5dNk6p/h2HL8MVmmWG8Vd0Ud2x1QQWwmIQr9OG/PXZ0AzsIIMV1YmvMJho
Decrypted text with PHP: The quick brown fox jumps over the lazy dog

Возможная часть дешифрования:

$key = mb_convert_encoding("This is a 256 bits = 32 byte key", "UTF-8");
$atomAES = new AtomAES();
$decrypt = $atomAES->decrypt("/XjXJc5dNk6p/h2HL8MVmmWG8Vd0Ud2x1QQWwmIQr9OG/PXZ0AzsIIMV1YmvMJho", $key);
echo $decrypt;

Тест 4: шифрование с помощью PHP / дешифрование с помощью Java (с использованием конкретного ключа выше)

Plain text: The quick brown fox jumps over the lazy dog
Encrypted text (base64 encoded) with PHP:  /XjXJc5dNk6p/h2HL8MVmmWG8Vd0Ud2x1QQWwmIQr9OG/PXZ0AzsIIMV1YmvMJho
Decrypted text with Java: The quick brown fox jumps over the lazy dog

Возможная часть шифрования:

$key = mb_convert_encoding("This is a 256 bits = 32 byte key", "UTF-8");
$atomAES = new AtomAES();
$encrypt = $atomAES->encrypt("The quick brown fox jumps over the lazy dog", $key);
echo $encrypt; 

Обновлено:

Аналог кода в основном методе (в сочетании с вашим образцом):

$encKey = mb_convert_encoding("VEMwcCYfFpsrXQVIFTDrA/2zP/5PYOY6JC1XEkEcLGSk/klt+HqHzGSr781Yznku", "UTF-8");
$asp_secret = mb_convert_encoding("DTosv9G179D0cY1985Uh2eF6ND80C95L", "UTF-8");

atomAES = new AtomAES();
$enc_key = $atomAES->decrypt($encKey, $asp_secret);
$enc_asp_secret = $atomAES->encrypt($asp_secret, base64_decode(base64_encode($enc_key)));
//$enc_asp_secret = $atomAES->encrypt($asp_secret, $enc_key);

echo "asp secret encrypted:\n".mb_convert_encoding($enc_asp_secret, "UTF-8")."\n"; 

Примечание. Выражение PHP base64_decode(base64_encode($enc_key)) эквивалентно $enc_key, поэтому вы также можете заменить его строкой, которая в настоящее время закомментирована. Единственная причина, по которой я его закодировал, состоит в том, что он также закодирован в коде Java. Здесь decodeBase64StringTOByte(encodeBase64String(enc_key) эквивалентен к enc_key. Это потому, что один метод противоположен другому.

Если вы запустите приведенный выше код, вывод будет

asp secret encrypted:
zAnTcjmAezfdzrWGixyfwmb8cM0otrsmwJ8+cNDs48Axh9hYgBtCJyeSE9tCvEBz

В качестве альтернативы вы можете определить третий метод класса AtomAES:

public function main(){
    $encKey = mb_convert_encoding("VEMwcCYfFpsrXQVIFTDrA/2zP/5PYOY6JC1XEkEcLGSk/klt+HqHzGSr781Yznku", "UTF-8");
    $asp_secret = mb_convert_encoding("DTosv9G179D0cY1985Uh2eF6ND80C95L", "UTF-8");

    $enc_key = $this->decrypt($encKey, $asp_secret);
    $enc_asp_secret = $this->encrypt($asp_secret, base64_decode(base64_encode($enc_key)));
    //$enc_asp_secret = $this->encrypt($asp_secret, $enc_key);

    echo "asp secret encrypted:\n".mb_convert_encoding($enc_asp_secret, "UTF-8")."\n"; 
}

который можно вызвать с помощью

$atomAES = new AtomAES();
$atomAES->main();

Я обновил вопрос с помощью зашифрованной строки, которую я получил с помощью кода Java. Я попытался расшифровать его, используя данный php-код. Но я получаю «ложный» ответ.

Ajeesh 19.12.2018 11:40

К сожалению, предоставленная зашифрованная строка неверна. Я обновил вопрос, указав фактическую строку, которую я получил после шифрования с использованием указанного выше ключа и строки.

Ajeesh 20.12.2018 09:47

это сработало! Спасибо за такой подробный ответ и приверженность. Скоро вручу награду :)

Ajeesh 21.12.2018 09:09

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