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

查看:16
本文介绍了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 = 数组('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 步: Url 编码结果.我用过 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(original, rfc[0], rfc[1]);var base64String = Convert.ToBase64String(加密);m_params = HttpUtility.UrlEncode(base64String, Encoding.UTF8);//将字节解密为字符串.字符串往返 = DecryptStringFromBytes(encrypted, rfc[0], rfc[1]);//显示原始数据和解密后的数据.Console.WriteLine("Original: {0}", original);Console.WriteLine("往返:{0}",往返);}静态字节[] EncryptStringToBytes(字符串纯文本,字节[] 密钥,字节[] IV){//检查参数.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");字节[] 加密;//创建一个 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 = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)){使用 (StreamWriter swEncrypt = new StreamWriter(csEncrypt)){//将所有数据写入流.swEncrypt.Write(纯文本);}加密 = 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();for (int i = 0; i < hashBytes.Length; i++){sb.Append(hashBytes[i].ToString(X2"));}返回 sb.ToString();}}公共静态动态创建密钥(字符串密码){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);动态[] arr = 新动态[2];arr[0] = 键;arr[1] = IV;返回 arr;}}

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

解决方案

正如 James K. Polk 总统的评论中提到的,256 位块大小的 Rijndael 仅在 .NET Framework 中支持,在 .NET Core 中不支持.您没有指定您正在运行的版本,但由于您在发布的代码中使用了 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";};string 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));string passwordHashHex = BitConverter.ToString(passwordHash).Replace("-", "").ToLower();//将字节数组转换为小写的十六进制字符串,就像在 PHP 中一样byte[] key = Encoding.UTF8.GetBytes(passwordHashHex);

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

加密的一种可能实现是:

使用系统;使用 System.IO;使用 System.Web;使用 System.Text;使用 System.Security.Cryptography;...字节 [] 加密 = 空;使用 (RijndaelManaged rijndael = new RijndaelManaged()){rijndael.Key = 键;rijndael.Mode = CipherMode.ECB;//默认值:CBCrijndael.BlockSize = 256;//默认值:128rijndael.Padding = PaddingMode.Zeros;//默认值:PKCS7ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, null);使用 (MemoryStream msEncrypt = new MemoryStream()){使用 (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)){使用 (StreamWriter swEncrypt = new StreamWriter(csEncrypt)){swEncrypt.Write(jsonEncoded);}加密 = msEncrypt.ToArray();}}}字符串 base64String = Convert.ToBase64String(encrypted);字符串 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)));打印($m_params.\n");

注意,C#使用小写字母作为url编码,而PHP使用大写字母,代表相同的url编码,见RFC 3986,秒.2.1.如果 C# 代码也应该对 url 编码应用大写字母,则可以使用正则表达式轻松实现,请参见例如此处.


关于安全的几点说明:

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

MD5 作为密钥派生函数 (KDF) 也是不安全的.在这里,应该使用可靠的 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天全站免登陆