#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);