openssl_encrypt шифрование AES без вектора - PullRequest
0 голосов
/ 16 ноября 2018

Мне нужен токен для доступа к некоторому источнику. Поставщик исходного кода дал мне пример Java, который показывает, как его создать. Вот как шифруется текст:

private static final String ALGO = "AES";                   
public static String encrypt(byte[] keyValue, String token_text) throws Exception {                 
    Key key = new SecretKeySpec(keyValue, ALGO);
    Cipher c = Cipher.getInstance(ALGO);
    c.init(Cipher.ENCRYPT_MODE, key);

    byte[] bytes = c.doFinal(token_text.getBytes());
    byte[] buf = Base64.encodeBase64(bytes);

    return new String(buf); 
}

Я хочу перевести эту часть Java-программы на php. Итак, вот мой код:

public static function generate_token($token_text, $key) {
        $iv = self::hex_to_str("00000000000000000000000000000000");
        $token = openssl_encrypt($token_text, "AES-128-CBC", $key, 0, $iv);
        return $token;
    }

    private static function hex_to_str($hex)
    {
            $string='';
            for ($i=0; $i < strlen($hex)-1; $i+=2)
            {
                $string .= chr(hexdec($hex[$i].$hex[$i+1]));
            }
            return $string;
    }

Но я не могу получить тот же результат. Я искал вокруг. Похоже, что cipher.init вызывается без вектора. Если я сделаю то же самое с openssl_encrypt, он выдаст мне ошибку:

openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended

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

java: v8GhW0lu8DzNyqsfQTg4g7H6pwXCAAgy9vqFdz5OmXY =

php: v8GhW0lu8DzNyqsfQTg4g6If77f + 8YVCcq8VcQGNe68 =

Я застрял здесь на целый день. Буду очень признателен, если кто-нибудь сможет помочь.

# java -version
openjdk version "1.8.0_101"
OpenJDK Runtime Environment (build 1.8.0_101-b13)
OpenJDK 64-Bit Server VM (build 25.101-b13, mixed mode)

# php -v
PHP 5.6.24 (cli) (built: Jul 21 2016 07:42:08) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

1 Ответ

0 голосов
/ 17 ноября 2018

В предположении, что сторона Java использует провайдер SunJCE, режимом по умолчанию является режим ECB, а заполнением по умолчанию является PKCS5Padding, т.е. Cipher.getInstance("AES") идентичен Cipher.getInstance("AES/ECB/PKCS5Padding"), см., Например, https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#ciphertrans.

В режиме ECB не используется IV.Таким образом, вы должны заменить в своем PHP метод generate_token

$iv = self::hex_to_str("00000000000000000000000000000000");
$token = openssl_encrypt($token_text, "AES-128-CBC", $key, 0, $iv);

на

$token = openssl_encrypt($token_text, "AES-128-ECB", $key, 0);

Пример:

encrypt("This is the key!".getBytes(), "This is a plain text that needs to be encrypted...");

обеспечивает

fLSh/HoQkrsIVBtZJVnuIRqcz4ztUBDkDG9Pi3xe49Q9hh9zDzWZDRHEO70ixfLf2WbWYSeDOQ/ONFTWHW9i0Q==

, который идентичен результату кода PHP (для того же ключа и обычного текста).

Как правило, режим ECB небезопасен, если у вас более одного блока (из ваших примеров я делаю выводчто ваш токен состоит как минимум из двух блоков).Тогда лучшим выбором был бы режим CBC или GCM, см., Например, https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption и https://security.stackexchange.com/questions/6500/aes-ecb-mode-for-single-block-random-data-encryption. Но так как метод шифрования Java, кажется, является вашей ссылкой, вероятно, нет никакого способа изменить его.

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

Класс шифрования Java автоматически определяет с размером ключа, нужно ли использовать AES-128, AES-192 или AES-256.Следовательно, вы также должны знать размер ключа в контексте кода Java.Если размер ключа составляет 16 байт, то выбор AES-128-ECB является правильным, в противном случае вам необходимо соответствующим образом настроить свой код PHP (например, AES-192-ECB или AES-256-ECB для 24-байтового или 32-байтового размера ключа,соответственно).

...