Как правильно вызвать метод openssl_decrypt, если у меня есть строка base64, содержащая данные из IV и зашифрованные данные?

#c# #php #encryption #rijndael #rijndaelmanaged

Вопрос:

У меня есть код, в C# котором зашифрована и расшифрована строка:

 private readonly UTF8Encoding _encoder;
private readonly ICryptoTransform _encryptor;
private readonly RijndaelManaged _rijndael;

public Crypter()
{
    _rijndael = new RijndaelManaged { Key = { 1, 2, 3, 4, ..., 16 } };
    _rijndael.GenerateIV();
    _encryptor = _rijndael.CreateEncryptor();
    _encoder = new UTF8Encoding();
}

public string Encrypt(string unencrypted)
    => Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
        
private byte[] Encrypt(byte[] buffer)
{
    byte[] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
    return _rijndael.IV.Concat(inputBuffer).ToArray();
}

public string Decrypt(string encrypted)
    => _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
        
private byte[] Decrypt(byte[] buffer)
{
    byte[] iv = buffer.Take(16).ToArray();
    using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
    {
        return decryptor.TransformFinalBlock(buffer, 16, buffer.Length - 16);
    }
}
 

Если вы проверите Decrypt(byte[] buffer) , я возьму первые 16 байтов, что равно IV.

Теперь я аналогично хочу реализовать в PHP (представьте, что я закодирую на C# стороне и отправлю его на сервер, который работает на php, где я хочу расшифровать его обратно). В качестве параметра для моей функции дешифрования PHP будет выводиться C# public string Encrypt(string unencrypted) . Мне каким-то образом нужно получить эти 16 байтов, чтобы получить IV и остальную часть, которые я соответственно передам как $data и $iv параметры для $decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, 0, $iv); функции

Я пытался использовать что-то подобное (используя распаковку):

 $stringValue = base64_decode($encrypted_data, true);
$integers = unpack("s*", $stringValue);
 

а затем попытался взять 16 первых чисел и каким-то образом преобразовать их обратно методом пакета. Но, вероятно, у меня недостаточно знаний.

Не могли бы вы, пожалуйста, помочь мне с этим?

P.S. Это я попробовал, основываясь на ответе и комментариях Ильи.

 $cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);
 

также, если я отлажу код и проверю, какие байты используются в $iv этом коде:

 $stringValue = base64_decode($iv, true);
$integers = unpack("C*", $encrypted_data);
 

и по сравнению с C# этим массивом байтов byte[] iv = buffer.Take(16).ToArray(); они равны , тогда я ожидаю, что я использую неправильный openssl_decrypt метод

Комментарии:

1. Похоже, вы используете 16-байтовый ключ в коде C#. Затем вы должны использовать aes-128-cbc в коде PHP (в противном случае PHP автоматически заполняет значения 0x00 до 32 байт, чтобы использовались разные ключи).

2. @Topaco, я пытался $cipher = "aes-128-cbc"; , но мне не повезло. Также я немного не понял, если я использую OPENSSL_RAW_DATA, то я не могу кодировать $данные, правильно?

3. Работает на моей машине (используя ваш код C# и PHP и предлагаемые изменения). Сколько байтов у вашего ключа?

4. OPENSSL_RAW_DATA должно быть установлено, если вы передаете необработанные данные, что вы и делаете (так как вы выполняете декодирование Base64 перед расшифровкой).

5. @Topaco 32 байта, хорошо, thx, тогда я правильно понял насчет OPENSSL_RAW_DATA

Ответ №1:

В php любая строка-это просто последовательность байтов, поэтому вы можете работать с ней напрямую, например, получать доступ к одному байту по его индексу или использовать substr для обрезки некоторого количества байтов. Пример:

 $str = 'some text or binary data received by http';
$first16Bytes = substr($str, 0, 16);
 

Комментарии:

1. Вы имеете в виду, что substr возвращается false ? Может быть, некоторые из предыдущих шагов вернули пустую строку? Попробуйте получить результат var_dump после каждого шага и посмотрите, что пошло не так.

2. Также substr может возвращать значение false, если общая длина строки меньше заданного смещения.

3. Нет, openssl_decrypt возвращает значение false

4. Так что проблема не в том, чтобы получить первые 16 байтов, верно?

5. @Aleksandrs — Код в комментарии трудно прочитать и, следовательно, лучше разместить в вопросе. openssl_decrypt() по умолчанию ожидает данные в кодировке Base64. Таким образом, Base64 кодирует $data или устанавливает OPENSSL_RAW_DATA в качестве 4-го параметра в openssl_decrypt() .

Ответ №2:

Во-первых, у меня возникла проблема с тем, как правильно извлечь 16 первых байтов из строки, потому что я думал, что делаю это неправильно. Спасибо от @Ильи Яценко за его ответ:

 $first16Bytes = substr($str, 0, 16);
 

Но потом я понял, что неправильно использую openssl_decrypt() метод. Поговорив в комментариях, особенно с @Topaco, мы выяснили, в чем была проблема. Вот рабочий код:

 $cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, 
OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);