使用Rijndael的跨平台(php到C#.NET)加密/解密

时间:2022-03-11 18:31:24

I'm currently having a bit of problem with decrypting a message encrypted by php mcrypt. The php code is as following:

我目前在解密由php mcrypt加密的邮件方面遇到了一些问题。 php代码如下:

<?php
  //$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
  $iv = "45287112549354892144548565456541";
  $key = "anjueolkdiwpoida";
  $text = "This is my encrypted message";
  $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
  $crypttext = urlencode($crypttext);
  $crypttext64=base64_encode($crypttext);
  print($crypttext64) . "\n<br/>";
?>

The the encrypted message is then sent to a ASP.NET platform (C#). However, I'm having problem retaining the order of decryption (base64 decode to urldecode). The code I had in ASP.NET is as the following (iv and key is the same as in php):

然后将加密的消息发送到ASP.NET平台(C#)。但是,我在保留解密顺序(base64解码到urldecode)时遇到问题。我在ASP.NET中的代码如下(iv和key与php中的相同):

public string Decode(string str)
{
    byte[] decbuff = Convert.FromBase64String(str);
    return System.Text.Encoding.UTF8.GetString(decbuff);
}

static public String DecryptRJ256(string cypher, string KeyString, string IVString)
{

    string sRet = "";
    RijndaelManaged rj = new RijndaelManaged();
    UTF8Encoding encoding = new UTF8Encoding();


    try
    {
        //byte[] message = Convert.FromBase64String(cypher);
        byte[] message = encoding.GetBytes(cypher);

        byte[] Key = encoding.GetBytes(KeyString);
        byte[] IV = encoding.GetBytes(IVString);

        rj.Padding = PaddingMode.Zeros;
        rj.Mode = CipherMode.CBC;
        rj.KeySize = 256;
        rj.BlockSize = 256;
        rj.Key = Key;
        rj.IV = IV;
        MemoryStream ms = new MemoryStream(message);

        using (CryptoStream cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
        {
            using (StreamReader sr = new StreamReader(cs))
            {
                sRet = sr.ReadToEnd();
            }
        }

    }
    finally
    {
        rj.Clear();
    }

    return sRet;


}

string temp = DecryptRJ256(Server.UrlDecode(Decode(cypher)), keyString, ivString);

The problem I'm having is that after I recieved the encrypted message from php, I converted it into byte[] and then converted back to UTF8 encoded string so I can urldecode it. then I feed the result into the function where I converted the string back to byte[] and ran it through the decryption process. However, I can't get the desired result...any ideas?

我遇到的问题是,在我收到来自php的加密消息后,我将其转换为byte []然后转换回UTF8编码的字符串,以便我可以对其进行urldecode。然后我将结果提供给函数,我将字符串转换回byte []并在解密过程中运行它。但是,我无法得到理想的结果......任何想法?

Thanks in advance.

提前致谢。

1 个解决方案

#1


31  

Here I can see problems on both sides.

在这里,我可以看到双方的问题。

Please keep in mind that what you get when encoding is not string, but rather an array of bytes. So in PHP you don't need to urlencode cyphertext.

请记住,编码时得到的不是字符串,而是字节数组。所以在PHP中你不需要urlencode cyphertext。

base64 encoding is all you need. When you open base64_encode help you see

你只需要base64编码。当你打开base64_encode帮助时,你会看到

base64_encode Encodes the given data with base64. This encoding is designed to make binary data survive transport

base64_encode使用base64对给定数据进行编码。此编码旨在使二进制数据在传输中幸存

One more thing - to have your message decoded in .net with a correct length, you have to manually append it with padding characters. Default padding mode for RijndaelManaged is PKCS7, lets' stick with it. You have to extend your source string to even blocks with characters code equal to number of padding bytes.

还有一件事 - 要使用正确的长度在.net中解码您的消息,您必须手动使用填充字符附加它。 RijndaelManaged的默认填充模式是PKCS7,让我们坚持下去。您必须将源字符串扩展为偶数块,其字符代码等于填充字节数。

<?php
  $iv = "45287112549354892144548565456541";
  $key = "anjueolkdiwpoida";
  $text = "This is my encrypted message";

  // to append string with trailing characters as for PKCS7 padding scheme
  $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
  $padding = $block - (strlen($text) % $block);
  $text .= str_repeat(chr($padding), $padding);

  $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);

  // this is not needed here            
  //$crypttext = urlencode($crypttext);

  $crypttext64=base64_encode($crypttext);
  print($crypttext64) . "\n<br/>";
?>

At C# side you have casting from base64 to byte[] to string to byte[]. You have to do the first conversion from base64 to byte[] only. Remember, base64 is holding the cyphered text that is binary data, not string. Also please note that RijndaelManaged is IDisposable, so I have wrapped it in using() construct. Calling Close() is necessary but not enough as stated in MSDN.

在C#端,你可以从base64到byte []到string到byte []。您必须仅执行从base64到byte []的第一次转换。请记住,base64保存的是包含二进制数据的加密文本,而不是字符串。另请注意,RijndaelManaged是IDisposable,所以我将它包装在using()构造中。调用Close()是必要的,但不如MSDN中所述。

public byte[] Decode(string str)
{
    var decbuff = Convert.FromBase64String(str);
    return decbuff;
}

static public String DecryptRJ256(byte[] cypher, string KeyString, string IVString)
{
    var sRet = "";

    var encoding = new UTF8Encoding();
    var Key = encoding.GetBytes(KeyString);
    var IV = encoding.GetBytes(IVString);

    using (var rj = new RijndaelManaged())
    {
        try
        {
            rj.Padding = PaddingMode.PKCS7;
            rj.Mode = CipherMode.CBC;
            rj.KeySize = 256;
            rj.BlockSize = 256;
            rj.Key = Key;
            rj.IV = IV;
            var ms = new MemoryStream(cypher);

            using (var cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
            {
                using (var sr = new StreamReader(cs))
                {
                    sRet = sr.ReadLine();
                }
            }
        }
        finally
        {
            rj.Clear();
        }
    }

    return sRet;
}

As a result, following code in C# will return you the initial string:

因此,在C#中使用以下代码将返回初始字符串:

var iv = "45287112549354892144548565456541";
var key = "anjueolkdiwpoida";
var cypher = "u+rIlHB/2rrT/u/qFInnlEkg2unhizsNzGVb9O54sP8=";

var temp = DecryptRJ256(Decode(cypher), key, iv);

#1


31  

Here I can see problems on both sides.

在这里,我可以看到双方的问题。

Please keep in mind that what you get when encoding is not string, but rather an array of bytes. So in PHP you don't need to urlencode cyphertext.

请记住,编码时得到的不是字符串,而是字节数组。所以在PHP中你不需要urlencode cyphertext。

base64 encoding is all you need. When you open base64_encode help you see

你只需要base64编码。当你打开base64_encode帮助时,你会看到

base64_encode Encodes the given data with base64. This encoding is designed to make binary data survive transport

base64_encode使用base64对给定数据进行编码。此编码旨在使二进制数据在传输中幸存

One more thing - to have your message decoded in .net with a correct length, you have to manually append it with padding characters. Default padding mode for RijndaelManaged is PKCS7, lets' stick with it. You have to extend your source string to even blocks with characters code equal to number of padding bytes.

还有一件事 - 要使用正确的长度在.net中解码您的消息,您必须手动使用填充字符附加它。 RijndaelManaged的默认填充模式是PKCS7,让我们坚持下去。您必须将源字符串扩展为偶数块,其字符代码等于填充字节数。

<?php
  $iv = "45287112549354892144548565456541";
  $key = "anjueolkdiwpoida";
  $text = "This is my encrypted message";

  // to append string with trailing characters as for PKCS7 padding scheme
  $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
  $padding = $block - (strlen($text) % $block);
  $text .= str_repeat(chr($padding), $padding);

  $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);

  // this is not needed here            
  //$crypttext = urlencode($crypttext);

  $crypttext64=base64_encode($crypttext);
  print($crypttext64) . "\n<br/>";
?>

At C# side you have casting from base64 to byte[] to string to byte[]. You have to do the first conversion from base64 to byte[] only. Remember, base64 is holding the cyphered text that is binary data, not string. Also please note that RijndaelManaged is IDisposable, so I have wrapped it in using() construct. Calling Close() is necessary but not enough as stated in MSDN.

在C#端,你可以从base64到byte []到string到byte []。您必须仅执行从base64到byte []的第一次转换。请记住,base64保存的是包含二进制数据的加密文本,而不是字符串。另请注意,RijndaelManaged是IDisposable,所以我将它包装在using()构造中。调用Close()是必要的,但不如MSDN中所述。

public byte[] Decode(string str)
{
    var decbuff = Convert.FromBase64String(str);
    return decbuff;
}

static public String DecryptRJ256(byte[] cypher, string KeyString, string IVString)
{
    var sRet = "";

    var encoding = new UTF8Encoding();
    var Key = encoding.GetBytes(KeyString);
    var IV = encoding.GetBytes(IVString);

    using (var rj = new RijndaelManaged())
    {
        try
        {
            rj.Padding = PaddingMode.PKCS7;
            rj.Mode = CipherMode.CBC;
            rj.KeySize = 256;
            rj.BlockSize = 256;
            rj.Key = Key;
            rj.IV = IV;
            var ms = new MemoryStream(cypher);

            using (var cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
            {
                using (var sr = new StreamReader(cs))
                {
                    sRet = sr.ReadLine();
                }
            }
        }
        finally
        {
            rj.Clear();
        }
    }

    return sRet;
}

As a result, following code in C# will return you the initial string:

因此,在C#中使用以下代码将返回初始字符串:

var iv = "45287112549354892144548565456541";
var key = "anjueolkdiwpoida";
var cypher = "u+rIlHB/2rrT/u/qFInnlEkg2unhizsNzGVb9O54sP8=";

var temp = DecryptRJ256(Decode(cypher), key, iv);