Как я могу расшифровать зашифрованное значение MCRYPT_RIJNDAEL_256 в С#, которое было зашифровано mcrypt в PHP?
Я пытаюсь прочитать значение Base64-Encoded из таблицы базы данных, управляемой на стороне Linux. В этом
В таблице есть столбец с именем first_name. На стороне Linux я могу легко ее расшифровать, используя следующую команду в PHP:
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, "patient_fn_salt",
base64_decode("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo="),
MCRYPT_MODE_ECB);
Однако я стараюсь как можно больше дублировать эту логику на стороне С#, и все, что я получаю, - тарабарщина.
Мой код С# ниже, я надеюсь, что у вас есть предложения, потому что у меня кончились идеи: (
byte [] cipherText =
Convert.FromBase64String("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo=");
byte [] key = Encoding.UTF8.GetBytes("patient_fn_salt");
Array.Resize(ref key, 32);
byte [] iv = new byte[32];
string fname = Utilities.Decrypt(cipherText, key, iv);
public static string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// TDeclare the streams used
// to decrypt to an in memory
// array of bytes.
MemoryStream msDecrypt = null;
CryptoStream csDecrypt = null;
StreamReader srDecrypt = null;
// Declare the AesManaged object
// used to decrypt the data.
RijndaelManaged rj = new RijndaelManaged();
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// Create an AesManaged object
// with the specified key and IV.
rj.Mode = CipherMode.ECB;
rj.BlockSize = 256;
rj.KeySize = 256;
rj.Padding = PaddingMode.Zeros;
rj.Key = Key;
rj.GenerateIV();
//rj.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rj.CreateDecryptor(rj.Key, rj.IV);
// Create the streams used for decryption.
msDecrypt = new MemoryStream(cipherText);
csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
srDecrypt = new StreamReader(csDecrypt);
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
finally
{
// Clean things up.
// Close the streams.
if (srDecrypt != null)
srDecrypt.Close();
if (csDecrypt != null)
csDecrypt.Close();
if (msDecrypt != null)
msDecrypt.Close();
// Clear the AesManaged object.
if (rj != null)
rj.Clear();
}
return plaintext;
}
}
Ответы
Ответ 1
Как говорит Paŭlo, режим ECB не использует IV. Если С# настаивает на одном, тогда используйте все нулевые байты.
Ключ "patient_fn_salt" составляет 15 символов, 120 бит. Функция дешифрования ожидает 256 бит ключа. Вы должны быть очень уверены, что дополнительные биты идентичны в обеих системах и добавляются в одно и то же место в обеих системах. Даже однократное неправильное приведет к расшифровке мусора. Внимательно прочитайте документацию PHP, чтобы точно определить, как "patient_fn_salt" будет расширен до 256-битного ключа. В частности, проверьте, действительно ли текущий ключ SHA256("patient_fn_salt")
.
В стороне, режим ECB небезопасен. Используйте режим CTR или режим CBC. Режим CTR не требует отступов, поэтому, вероятно, будет означать меньше cyphertext для хранения.
ETA: при повторном чтении я замечаю, что сторона С# заполняет нулями. Какое дополнение используется PHP-стороной? Нулевая прокладка - это не очень хорошая идея, поскольку она не может распознать ошибочное дешифрование. Заполнение PKCS7 имеет гораздо больше шансов распознать неисправный выход. Лучше явно указывать отступы на обоих концах, а не полагаться на значения по умолчанию.
Ответ 2
Сообщение устарело, но это может помочь кому-то в будущем. Эта функция шифрует точно так же, как и mcrypt_encrypt с параметрами MCRYPT_RIJNDAEL_256 и MCRYPT_MODE_ECB
static byte[] EncryptStringToBytes(string plainText, byte[] key)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
byte[] encrypted;
using (var rijAlg = new RijndaelManaged())
{
rijAlg.BlockSize = 256;
rijAlg.Key = key;
rijAlg.Mode = CipherMode.ECB;
rijAlg.Padding = PaddingMode.Zeros;
rijAlg.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
swEncrypt.Write(plainText);
encrypted = msEncrypt.ToArray();
}
}
return encrypted;
}
И вот функция для его дешифрования
static string DecryptStringFromBytes(byte[] cipherText, byte[] key)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
string plaintext;
using (var rijAlg = new RijndaelManaged())
{
rijAlg.BlockSize = 256;
rijAlg.Key = key;
rijAlg.Mode = CipherMode.ECB;
rijAlg.Padding = PaddingMode.Zeros;
rijAlg.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
using (var msDecrypt = new MemoryStream(cipherText))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var srDecrypt = new StreamReader(csDecrypt))
plaintext = srDecrypt.ReadToEnd();
}
return plaintext;
}