php to C#使用Rijindael-256算法加密并使用base64算法编码的JSON数据数组 [英] php to C# A JSON array of data encrypted using the Rijindael-256 algorithm and encoded using a base64 algorithm

查看:114
本文介绍了php to C#使用Rijindael-256算法加密并使用base64算法编码的JSON数据数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将以下php代码转换为C#:

  $ m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$ key,json_encode($ arParams),MCRYPT_MODE_ECB)))); 

文档内容:

m_params:的JSON数据数组附加参数使用加密Rijindael-256算法和使用base64编码算法.

我假设了什么?

步骤1:创建一个参数数组,即$ arParams对于php,其声明如下:

  $ arParams = array('success_url'=>"http://google.com/new_success_url",'fail_url'=>"http://google.com/new_fail_url",'status_url'=>"http://google.com/new_status_url",); 

对于C#,我已经这样声明了:

  var Additional_params =新对象[]{新的{" http://google.com/new_success_url"},新的{" http://google.com/new_fail_url"},新的{" http://google.com/new_status_url"},}; 

步骤2:编码为JSON字符串,我使用了 JsonConvert.SerializeObject(additional_params);

步骤3:使用RIJNDAEL-256算法和ECB加密结果(我也使用过CBC)

第4步:使用base64对结果进行编码.我用过 Convert.ToBase64String(encrypted);

步骤5:使用网址对结果进行编码.我已经使用 HttpUtility.UrlEncode(base64String,Encoding.UTF8);

步骤6:将结果保存到 m_params

我当前的代码如下:

  var Additional_params =新对象[]{新的{" http://google.com/new_success_url"},新的{" http://google.com/new_fail_url"},新的{" http://google.com/new_status_url"},};字符串m_params =";//将其他参数转换为Json对象var jsonEncoded = JsonConvert.SerializeObject(additional_params);尝试{字符串原始= jsonEncoded;//创建RijndaelManaged的新实例//班级.这将生成一个新密钥并进行初始化//向量(IV).使用(RijndaelManaged myRijndael = new RijndaelManaged()){var final_Key = CreateMD5(payeer.m_key + payeer.m_orderid);var rfc = CreateKey(final_Key);//将字符串加密为字节数组.byte []加密= EncryptStringToBytes(原始,rfc [0],rfc [1]);var base64String = Convert.ToBase64String(加密);m_params = HttpUtility.UrlEncode(base64String,Encoding.UTF8);//将字节解密为字符串.字符串往返= DecryptStringFromBytes(已加密,rfc [0],rfc [1]);//显示原始数据和解密的数据.Console.WriteLine(原始:{0}",原始);Console.WriteLine(往返:{0}",往返);}静态byte [] EncryptStringToBytes(字符串plainText,byte []密钥,byte [] IV){//检查参数.如果(plainText == null || plainText.Length< = 0)抛出新的ArgumentNullException("plainText");如果(键== null || Key.Length< = 0)抛出新的ArgumentNullException("Key");如果(IV == null || IV.Length <= 0)抛出新的ArgumentNullException("IV");byte []加密;//创建一个RijndaelManaged对象//使用指定的键和IV.使用(RijndaelManaged rijAlg = new RijndaelManaged()){rijAlg.Key =键;rijAlg.IV = IV;rijAlg.Mode = CipherMode.ECB;//rijAlg.KeySize = 256;rijAlg.BlockSize = 256;rijAlg.Padding = PaddingMode.PKCS7;//创建一个加密器以执行流转换.ICryptoTransform加密器= rijAlg.CreateEncryptor(rijAlg.Key,rijAlg.IV);//创建用于加密的流.使用(MemoryStream msEncrypt = new MemoryStream()){使用(CryptoStream csEncrypt =新的CryptoStream(msEncrypt,加密器,CryptoStreamMode.Write)){使用(StreamWriter swEncrypt = new StreamWriter(csEncrypt)){//将所有数据写入流.swEncrypt.Write(plainText);}加密= msEncrypt.ToArray();}}}//从内存流返回加密的字节.返回加密}公共静态字符串CreateMD5(字符串输入){//使用输入字符串来计算MD5哈希使用(System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()){byte [] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);byte [] hashBytes = md5.ComputeHash(inputBytes);//将字节数组转换为十六进制字符串StringBuilder sb = new StringBuilder();对于(int i = 0; i< hashBytes.Length; i ++){sb.Append(hashBytes [i] .ToString("X2")));}返回sb.ToString();}}公共静态动态CreateKey(字符串密码){var salt = new byte [] {1,2,23,234,37,48,134,63,248,4};const int迭代数= 9872;使用(var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password,salt,Iterations)){var key = rfc2898DeriveBytes.GetBytes(32);var IV = rfc2898DeriveBytes.GetBytes(16);dynamic [] arr =新的dynamic [2];arr [0] =键;arr [1] = IV;返回arr;}} 

它没有提供相同的输出.我错过了什么吗?

解决方案

正如总统詹姆斯·K·波尔克(James K. Polk)的评论中所述,只有.NET Framework(而不是.NET Core)支持Rijndael,其块大小为256位.您没有指定正在运行的版本,但是由于在发布的代码中使用了256位的块大小( rijAlg.BlockSize = 256; ),因此我假设您正在运行.NET Framework(否则),则需要应用支持Rijndael且块大小为256位的第三方库,例如BouncyCastle/C#).

两个代码都使用不同的填充. mcrypt 默认情况下应用零填充,C#代码显式使用PKCS7填充(这也是C#默认值).为了使C#代码提供与PHP代码相同的结果,有必要在C#代码中切换到零填充(应注意,与PKCS7填充不同,零填充是不可靠的).

实例化 additional_params (顺便说一句,它无法在我的计算机上编译)时,变量名丢失,因此在序列化中也丢失了它们.可以改用匿名类型.另外,请注意,默认情况下, json_encode()会转义斜杠(/),即将其转换为 \/手动使用C#代码,例如与 Replace("/","\\/")一起使用.JSON序列化的一种可能实现是:

 使用Newtonsoft.Json;...var AdditionalParams =新{success_url =" http://google.com/new_success_url" ;,fail_url =" http://google.com/new_fail_url" ;,status_url =" http://google.com/new_status_url"};字符串jsonEncoded = JsonConvert.SerializeObject(additionalParams).Replace("/","\\/"); 

在PHP代码中,密钥是使用MD5摘要从密码派生的.默认情况下, md5() 返回结果为十六进制字符串,该字符串将16个字节的哈希值转换为用作密钥的32个字节的值,因此使用AES-256.PHP用小写字母表示十六进制数字,还必须在C#代码中相应地实现该数字,例如:

 使用系统;使用System.Text;使用System.Security.Cryptography;...MD5 md5 = MD5.Create();字符串密码=我的密码";//测试密码byte [] passwordHash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));字符串passwordHashHex = BitConverter.ToString(passwordHash).Replace(-",").ToLower();//将字节数组转换为小写的十六进制字符串,如PHP中一样byte []键= Encoding.UTF8.GetBytes(passwordHashHex); 

其中使用 BitConverter 完成字节数组到十六进制字符串的转换,请参见此处.

加密的一种可能的实现方式是:

 使用系统;使用System.IO;使用System.Web;使用System.Text;使用System.Security.Cryptography;...byte []已加密= null;使用(RijndaelManaged rijndael = new RijndaelManaged()){rijndael.Key =键;rijndael.Mode = CipherMode.ECB;//默认值:CBCrijndael.BlockSize = 256;//默认值:128rijndael.Padding = PaddingMode.Zeros;//默认值:PKCS7ICryptoTransform加密器= rijndael.CreateEncryptor(rijndael.Key,null);使用(MemoryStream msEncrypt = new MemoryStream()){使用(CryptoStream csEncrypt =新的CryptoStream(msEncrypt,加密器,CryptoStreamMode.Write)){使用(StreamWriter swEncrypt = new StreamWriter(csEncrypt)){swEncrypt.Write(jsonEncoded);}加密= msEncrypt.ToArray();}}}字符串base64String = Convert.ToBase64String(加密);字符串m_params = HttpUtility.UrlEncode(base64String,Encoding.UTF8);Console.WriteLine(m_params); 

此代码和使用的测试密码会产生以下结果:

<预类= 朗 - 无prettyprint-越权"> <代码> C3pldgsLDSqfG28cbt%2fv0uiBNQT6cWn86iRwg%2bv2blTzR7Lsnra%2b2Ok35Ex9f9UbG%2bjhKgITUQ8kO3DrIrWUQWirzYzwGBucHNRThADf60rGUIBDdjZ2kOIhDVXUzlMsZtBvYIgFoIqFJXCbhZq9GGnKtABUOa5pcmIYeUn%2B%2fqG1mdtJenP5vt8n0eTxsAd6CFc1%2bguR0wZx%2fEZAMsBBRw%3D%3D

根据以下PHP代码的结果:

  $ key = md5('我的密码');//测试密码$ arParams =数组('success_url'=>"http://google.com/new_success_url",'fail_url'=>"http://google.com/new_fail_url",'status_url'=>"http://google.com/new_status_url",);$ m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$ key,json_encode($ arParams),MCRYPT_MODE_ECB)));;print($ m_params."\ n"); 

请注意,C#使用小写字母表示url编码,而PHP使用大写字母表示相同的url编码,请参见此处.


关于安全性的几点评论:

PHP代码应用了不安全的ECB模式.出于安全原因,应使用具有IV的模式,例如CBC或GCM.后者提供隐式身份验证加密.IV是为每种加密随机生成的,不是秘密的,并且会与密文(通常是前置的)一起发送给接收者.

作为密钥派生函数(KDF)的MD5也不安全.在此,应使用可靠的KDF,例如PBKDF2.

此外,使用十六进制字符串作为键会弱化密钥,因为每个字节都减少为十六进制数字系统的16个值.使用KDF生成的二进制数据更为安全,这样每个字节可以采用256个不同的值.

mcrypt 是不推荐使用.可能的替代方法是 openssl

I am trying to convert the following php code to C#:

 $m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$key, json_encode($arParams), MCRYPT_MODE_ECB)));

What the documentation says:

m_params : A JSON array of data of additional parameters encrypted using the Rijindael-256 algorithm and encoded using a base64 algorithm.

What I've assumed?

Step 1: Create an array of params i.e. $arParams For php its declared like:

$arParams = array(
'success_url' => 'http://google.com/new_success_url',
'fail_url' => 'http://google.com/new_fail_url',
'status_url' => 'http://google.com/new_status_url',
);

For C# I've declared it like this:

 var additional_params = new object[]
                        {
                            new {"http://google.com/new_success_url"},
                            new {"http://google.com/new_fail_url"},
                            new {"http://google.com/new_status_url"},
                        };

Step 2: Encode to JSON string, I've used JsonConvert.SerializeObject(additional_params);

Step 3: Encrypt the result using RIJNDAEL-256 Algorithm using ECB (I've used CBC as well)

Step 4: Encode the result using base64. I've used Convert.ToBase64String(encrypted);

Step 5: Url encode the result. I've used HttpUtility.UrlEncode(base64String, Encoding.UTF8);

Step 6: Save the result in m_params

My current code looks like this:

                var additional_params = new object[]
                    {
                        new {"http://google.com/new_success_url"},
                        new {"http://google.com/new_fail_url"},
                        new {"http://google.com/new_status_url"},
                    };
                string m_params ="";
                //converting to Json object additional params
                var jsonEncoded = JsonConvert.SerializeObject(additional_params);
                try
                {

                    string original = jsonEncoded;

                    // Create a new instance of the RijndaelManaged
                    // class.  This generates a new key and initialization
                    // vector (IV).
                    using (RijndaelManaged myRijndael = new RijndaelManaged())
                    {
                        var final_Key = CreateMD5(payeer.m_key + payeer.m_orderid);
                        var rfc = CreateKey(final_Key);
                        
                        // Encrypt the string to an array of bytes.
                        byte[] encrypted = EncryptStringToBytes(original, rfc[0], rfc[1]);
                        var base64String = Convert.ToBase64String(encrypted);
                        m_params = HttpUtility.UrlEncode(base64String, Encoding.UTF8);
                        // Decrypt the bytes to a string.
                        string roundtrip = DecryptStringFromBytes(encrypted, rfc[0], rfc[1]);

                        //Display the original data and the decrypted data.
                        Console.WriteLine("Original:   {0}", original);
                        Console.WriteLine("Round Trip: {0}", roundtrip);
                    }

         static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
           {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;
            // Create an RijndaelManaged object
            // with the specified key and IV.
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;
                rijAlg.Mode = CipherMode.ECB;
              //  rijAlg.KeySize = 256;
                rijAlg.BlockSize = 256;
                rijAlg.Padding = PaddingMode.PKCS7;
                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }


public static string CreateMD5(string input)
        {
            // Use input string to calculate MD5 hash
            using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                // Convert the byte array to hexadecimal string
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    sb.Append(hashBytes[i].ToString("X2"));
                }
                return sb.ToString();
            }
        }
        public static dynamic CreateKey(string password)
        {
            var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };

            const int Iterations = 9872;
            using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
            {
                var key = rfc2898DeriveBytes.GetBytes(32);
                var IV = rfc2898DeriveBytes.GetBytes(16);
                dynamic[] arr = new dynamic[2];
                arr[0] = key;
                arr[1] = IV;
                return arr;
            }
                
        }

Its not giving the same output. Am I missing something??

解决方案

As mentioned in President James K. Polk's comment, Rijndael with a block size of 256 bits is only supported in the .NET Framework, not in .NET Core. You did not specify the version you are running, but since you use a block size of 256 bits in the posted code (rijAlg.BlockSize = 256;), I assume you are running .NET Framework (otherwise, you need to apply a third party library that supports Rijndael with a block size of 256 bits, such as BouncyCastle/C#).

Both codes use a different padding. mcrypt applies Zero padding by default, the C# code explicitly uses PKCS7 padding (which is also the C# default). So that the C# code provides the same result as the PHP code, it is necessary to switch to Zero padding in the C# code (it should be noted that Zero padding is unreliable, unlike PKCS7 padding).

When additional_params is instantiated (which, by the way, does not compile on my machine), the variable names are missing, so they are also missing in the serialization. An anonymous type could be used instead. Also, note that json_encode() escapes the slash (/) by default, i.e. converts it to a \/, which has to be done manually in the C# code, e.g. with Replace("/", "\\/"). One possible implementation of the JSON serialization is:

using Newtonsoft.Json;
...
var additionalParams = new
{
    success_url = "http://google.com/new_success_url",
    fail_url = "http://google.com/new_fail_url",
    status_url = "http://google.com/new_status_url"
};
string jsonEncoded = JsonConvert.SerializeObject(additionalParams).Replace("/", "\\/");

In the PHP code, the key is derived from a password using the MD5 digest. By default, md5() returns the result as a hexadecimal string, which converts the 16 bytes hash into a 32 bytes value that is applied as the key, so that AES-256 is used. PHP represents the hexadecimal digits with lowercase letters, which must also be implemented accordingly in the C# code, e.g.:

using System;
using System.Text;
using System.Security.Cryptography;
...
MD5 md5 = MD5.Create();
string password = "My password"; // test password 
byte[] passwordHash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
string passwordHashHex = BitConverter.ToString(passwordHash).Replace("-", "").ToLower();  // convert byte array to lowercase hex string as in PHP
byte[] key = Encoding.UTF8.GetBytes(passwordHashHex);

where the conversion of the byte array to the hexadecimal string is done with BitConverter, see here.

A possible implementation for the encryption is:

using System;
using System.IO;
using System.Web;
using System.Text;
using System.Security.Cryptography;
...
byte[] encrypted = null;
using (RijndaelManaged rijndael = new RijndaelManaged())
{
    rijndael.Key = key;
    rijndael.Mode = CipherMode.ECB;           // default: CBC
    rijndael.BlockSize = 256;                 // default: 128
    rijndael.Padding = PaddingMode.Zeros;     // default: PKCS7

    ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, null);
    using (MemoryStream msEncrypt = new MemoryStream())
    {
        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
            {
                swEncrypt.Write(jsonEncoded);
            }
            encrypted = msEncrypt.ToArray();
        }
    }
}
string base64String = Convert.ToBase64String(encrypted);
string m_params = HttpUtility.UrlEncode(base64String, Encoding.UTF8);
Console.WriteLine(m_params);

where this code with the used test password gives the following result:

C3pldgsLDSqfG28cbt%2fv0uiBNQT6cWn86iRwg%2bv2blTzR7Lsnra%2b2Ok35Ex9f9UbG%2bjhKgITUQ8kO3DrIrWUQWirzYzwGBucHNRThADf60rGUIBDdjZ2kOIhDVXUzlMsZtBvYIgFoIqFJXCbhZq9GGnKtABUOa5pcmIYeUn%2b%2fqG1mdtJenP5vt8n0eTxsAd6CFc1%2bguR0wZx%2fEZAMsBBRw%3d%3d

in accordance with the result of the following PHP code:

$key = md5('My password'); // test password
$arParams = array(
    'success_url' => 'http://google.com/new_success_url',
    'fail_url' => 'http://google.com/new_fail_url',
    'status_url' => 'http://google.com/new_status_url',
);
$m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$key, json_encode($arParams), MCRYPT_MODE_ECB)));
print($m_params . "\n");

Note that C# uses lowercase letters for the url encoding, while PHP uses uppercase letters, which represents the same url encoding, see RFC 3986, sec. 2.1. If the C# code should nevertheless also apply uppercase letters for the url encoding, this can easily be achieved using regular expressions, see e.g. here.


A few remarks regarding security:

The PHP code applies the insecure ECB mode. For security reasons, a mode with an IV should be used, e.g. CBC or GCM. The latter provides implicit authenticated encryption. The IV is randomly generated for each encryption, is not secret and is sent to the recipient along with the ciphertext (usually prepended).

MD5 as a key derivation function (KDF) is also insecure. Here, a reliable KDF should be used, e.g. PBKDF2.

In addition, using the hexadecimal string as the key weakens the same, since each byte is reduced to the 16 values of the hexadecimal number system. More secure is the use of the binary data generated by the KDF, so that each byte can take 256 different values.

mcrypt is deprecated. A possible alternative is openssl.

这篇关于php to C#使用Rijindael-256算法加密并使用base64算法编码的JSON数据数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆