【问题标题】:How can I decrypt an encrypted MCRYPT_RIJNDAEL_256 value in C#, that was encrypted by mcrypt in PHP?如何解密 C# 中的加密 MCRYPT_RIJNDAEL_256 值,该值由 PHP 中的 mcrypt 加密?
【发布时间】:2011-08-16 18:50:57
【问题描述】:

我正在尝试从 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;
  }
 }

【问题讨论】:

  • 也许这会有所帮助。 stackoverflow.com/questions/202011/…
  • 请注意:您的方法中未使用IV 参数......并且ECB模式没有初始化向量。 (但我认为这与您的问题无关。)

标签: c# php cryptography aes


【解决方案1】:

帖子很旧,但这可能会对将来的某些人有所帮助。此函数的加密方式与 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;
    }

【讨论】:

  • 我希望这对任何人都没有用,因为如果是这样,这意味着他们正在使用非常有问题的加密货币。
【解决方案2】:

正如 Paŭlo 所说,ECB 模式不使用 IV。如果 C# 坚持使用 1,则使用所有零字节。

密钥“patient_fn_salt”是 15 个字符,120 位。您的解密函数需要 256 位密钥。您需要非常确保两个系统中的额外位相同,并且在两个系统中添加到相同的位置。即使是一点点错误也会导致垃圾解密。仔细阅读 PHP 文档,以确定如何将“patient_fn_salt”扩展为 256 位密钥。特别检查实际密钥是否为SHA256("patient_fn_salt")

顺便说一句,ECB 模式是不安全的。优先使用 CTR 模式或 CBC 模式。 CTR 模式不需要填充,因此可能意味着要存储的密文更少。

ETA:在另一次通读中,我注意到 C# 端用零填充。 PHP端使用什么填充?零填充不是一个好主意,因为它无法识别错误的解密。 PKCS7 填充有更好的机会识别错误的输出。最好明确指定两端的填充,而不是依赖默认值。

【讨论】:

  • documentation says: 如果它小于所需的键大小,则用 '\0' 填充。 ...并且 Array.Resize 看起来就像相同的。但也许显式填充更安全。
  • 如果可能的话,直接检查两个字节数组的内容会很有用。如果做不到这一点,显式填充当然值得尝试。
【解决方案3】:

在与旧系统通信时,我遇到了同样的问题。我不得不提出自己的解决方案,因为 dotnet core 似乎不支持 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;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-28
    • 1970-01-01
    • 2011-01-24
    • 2015-11-10
    • 2023-03-23
    • 2012-01-03
    • 2016-07-25
    • 2012-07-27
    相关资源
    最近更新 更多