开发者

How can I decrypt an encrypted MCRYPT_RIJNDAEL_256 value in C#, that was encrypted by mcrypt in PHP?

开发者 https://www.devze.com 2023-03-28 18:14 出处:网络
I am trying to read a Base64-Encoded value from a Database table managed on the Linux side. In that table there is a column called first_name. On the Linux side I can decrypt this easily by using the

I am trying to read a Base64-Encoded value from a Database table managed on the Linux side. In that table there is a column called first_name. On the Linux side I can decrypt this easily by using the following command in PHP:

$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, "patient_fn_salt",
                       base64_decode("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo="),
                       MCRYPT_MODE_ECB);

However I try as much as I can to duplicate this logic on the C# side and all I get is gibberish.

My C# code is below, i hope you have some suggestions because I ran out of ideas :(

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;
  }
 }


Post is old, but this might help somebody in a future. This function encrypt exactly like mcrypt_encrypt with parameters MCRYPT_RIJNDAEL_256 and 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;
    }

And here is function to decrypt it

     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;
    }


As Paŭlo says, ECB mode does not use an IV. If C# insists on one then use all zero bytes.

The key "patient_fn_salt" is 15 characters, 120 bits. Your decryption function is expecting 256 bits of key. You need to be very sure that the extra bits are identical in both systems and are being added in the same place in both systems. Even a single bit wrong will result in garbage decryption. Read the PHP documentation very carefully to determine exactly how "patient_fn_salt" is expanded to a 256 bit key. In particular check if the actual key is SHA256("patient_fn_salt").

As an aside, ECB mode is insecure. Use either CTR mode or CBC mode in preference. CTR mode does not require padding so will probably mean less cyphertext to store.

ETA: on another read through I notice that the C# side is padding with zeros. What padding is the PHP side using? Zero padding is not a good idea, as it cannot recognise a faulty decryption. PKCS7 padding has a much better chance of recognising a faulty output. Best to explicitly specify the padding on both ends rather than rely on defaults.


I hit the same problem when comunicating with a legacy system. I had to come up with my own solution as Rijndael with a Blocksize of 256 seems to be not supported by dotnet core.

Here is my solution using the bouncy castle library:

    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;
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消