[英]How can I decrypt an encrypted MCRYPT_RIJNDAEL_256 value in C#, that was encrypted by mcrypt in PHP?
我正在嘗試從 Linux 端管理的數據庫表中讀取 Base64 編碼值。 在該表中有一個名為 first_name 的列。 在 Linux 方面,我可以通過在 PHP 中使用以下命令輕松解密:
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, "patient_fn_salt",
base64_decode("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo="),
MCRYPT_MODE_ECB);
但是,我盡我所能在 C# 端復制此邏輯,但我得到的只是胡言亂語。
我的 C# 代碼如下,我希望你有一些建議,因為我沒有想法了:(
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;
}
}
帖子很舊,但這可能會在將來對某人有所幫助。 這個 function 加密完全像 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;
}
這是 function 解密它
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;
}
正如 Paŭlo 所說,ECB 模式不使用 IV。 如果 C# 堅持使用 1,則使用所有零字節。
密鑰“patient_fn_salt”是 15 個字符,120 位。 您的解密 function 需要 256 位密鑰。 您需要非常確定兩個系統中的額外位是相同的,並且在兩個系統中被添加到相同的位置。 即使是一點點錯誤也會導致垃圾解密。 仔細閱讀 PHP 文檔,以確定“patient_fn_salt”如何擴展為 256 位密鑰。 特別檢查實際密鑰是否為SHA256("patient_fn_salt")
。
順便說一句,歐洲央行模式是不安全的。 優先使用 CTR 模式或 CBC 模式。 CTR 模式不需要填充,因此可能意味着要存儲的密文更少。
ETA:在另一次通讀中,我注意到 C# 側用零填充。 PHP 端使用什么填充? 零填充不是一個好主意,因為它無法識別錯誤的解密。 PKCS7 填充有更好的機會識別有故障的 output。 最好明確指定兩端的填充,而不是依賴默認值。
在與遺留系統通信時,我遇到了同樣的問題。 我不得不提出自己的解決方案,因為 dotnet 核心似乎不支持 Blocksize 為 256 的 Rijndael。
這是我使用充氣城堡庫的解決方案:
public static byte[] Rijandael256Decrypt(byte[] inputBytes, byte[] keyBytes)
{
// set up
IBufferedCipher cipher = new PaddedBufferedBlockCipher(new RijndaelEngine(256), new ZeroBytePadding());
KeyParameter keyParam = new KeyParameter(keyBytes);
cipher.Init(false, keyParam);
int sizeAtLeastRequired = cipher.GetOutputSize(inputBytes.Length);
byte[] outputBytes = new byte[sizeAtLeastRequired];
// decrypt
int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
length += cipher.DoFinal(outputBytes, length);
// resize output
Array.Resize(ref outputBytes, length);
return outputBytes;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.