获取等于php十六进制字符串的C#字节数组 [英] Get a C# byte array equal to a php hex string

查看:91
本文介绍了获取等于php十六进制字符串的C#字节数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我现在不允许修改这段php代码,主要是因为它很旧并且可以正常工作。

So I have this piece of php code that I'm not allowed to modify for now, mainly because it's old and works properly.


警告!非常糟糕的代码说明。 IV不会被随机化,也不会与输出一起存储。我不是要问这个,因为我想问,因为我要问
。我还计划在工作时进行重构,并使用可靠的加密代码完成C#代码。

Warning! Very bad code overal. the IV is not being randomized neither stored with the output. I'm not asking this because I want to, I'm asking because I need to. I'm also planning on refactoring when I get this working and completing my C# code with actually reliable cyphering code.



function encrypt($string) 
{
    $output = false;
    $encrypt_method = "AES-256-CBC";
    $param1 = 'ASasd564D564aAS64ads564dsfg54er8G74s54hjds346gf445gkG7';
    $param2 = '654dsfg54er8ASG74sdfg54hjdas346gf34kjdDJF56hfs2345gkFG';
    $ky = hash('sha256', $param1); // hash
    $iv = substr(hash('sha256', $param2), 0, 16);

    $output = openssl_encrypt($string, $encrypt_method, $ky, 0, $iv);
    $output = base64_encode($output);
    return $output;
}    

我想在C#中做同样的事情,因为我要获得其所有字段均使用该代码加密。

I want to do the same in C# because I'm getting an entity with all its fields encrypted with that code.

我希望能够对该数据进行加密,因此我可以查询我的实体列表,而不必解密所有实体。而且我想解密过滤后的实体的某些属性,以便它们实际上可以有用。

I want to be able to encrypt that data so I can query my entity list whithout having to decrypt all the entities. And I want to decrypt some properties of the filtered entities so they can actually be useful.

现在,我为此创建了一个CryptoHelper来执行此操作,除了它没有

Now, for that matter I created a CryptoHelper that will do this, except it doesn't.

我尝试在构造函数中计算Key和IV:

I try to calculate the Key and IV in the constructor:

    public readonly byte[] Key;
    public readonly byte[] IV;

    public CryptoHelper()
    {
        Key = GetByteArraySha256Hash("ASasd564D564aAS64ads564dsfg54er8G74s54hjds346gf445gkG7", false);
        IV = GetByteArraySha256Hash("654dsfg54er8ASG74sdfg54hjdas346gf34kjdDJF56hfs2345gkFG", true);
    }

    private byte[] GetByteArraySha256Hash(string source, bool salt)
    {
        byte[] result;
        try
        {
            using (SHA256 sha256Hash = SHA256.Create())
            {
                result = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(source));
            }
        }
        catch (Exception)
        {
            throw;
        }
        if (salt)
        {
            return result.Take(16).ToArray();
        }
        return result;
    }

然后使用加密和解密方法,在我测试时效果很好它们带有测试字符串。唯一的问题是字符串的末尾有一些填充,但是考虑到任何用php方法加密的字符串都会产生乱码,这是一个小问题。

And then use a Encrypt and Decrypt methods that are working pretty well when I test them with a test string. The only problem is that the string have some padding at the end, but it's kind of a minor problem considering that any string encrypted with the php method results in gibberish.

    private string Encrypt(string source)
    {
        try
        {
            string result = "";

            using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros })
            {
                byte[] sourceByteArray = Encoding.UTF8.GetBytes(source);

                using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
                {
                    byte[] encriptedSource = encryptor.TransformFinalBlock(sourceByteArray, 0, sourceByteArray.Length);
                    result = Convert.ToBase64String(encriptedSource);
                    result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
                }
            }

            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

    private string Decrypt(string source)
    {
        try
        {
            string result = "";
            //Double Base64 conversion, as it's done in the php code.
            byte[] sourceByte = Convert.FromBase64String(source);
            byte[] sourceFreeOfBase64 = Convert.FromBase64String(Encoding.UTF8.GetString(sourceByte));

            byte[] resultByte;
            int decryptedByteCount = 0;

            using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros })
            {
                using (ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV))
                {
                    using (MemoryStream memoryStream = new MemoryStream(sourceFreeOfBase64))
                    {
                        using (CryptoStream cs = new CryptoStream(memoryStream, AESDecrypt, CryptoStreamMode.Read))
                        {
                            resultByte = new byte[sourceFreeOfBase64.Length];
                            decryptedByteCount = cs.Read(resultByte, 0, resultByte.Length);
                        }
                    }
                }

                //This returns the encoded string with a set of "\0" at the end.
                result = Encoding.UTF8.GetString(resultByte);
                result = result.Replace("\0", "");
            }

            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

我很确定这里的主要问题在于在php行 $ iv = substr(hash('sha256',$ param2),0,16); 中。我检查了php和C#中两个哈希函数的结果,并且结果完全相同。

I'm pretty sure that the main problem here lies in the php line $iv = substr(hash('sha256', $param2), 0, 16);. I checked the results of both hash functions in php and C# and are exactly the same.

从我一直在阅读的php中,将字符串当作字节数组对待(如果我错了),所以16个字符的字符串应该足以获取16个字节的数组和128个块。但是在C#中,当我得到16个字节的数组并将其转换为字符串时,我得到了一个32个字符的字符串,这与我执行 $ iv = substr(hash('sha256',$ param2 ),0,32);

From what I've been reading php treats strings as byte arrays (correct me if I'm wrong) so a 16 char string should be enough to get a 16 byte array and a 128 block. But in C#, when I get the 16 byte array and convert it to a string I get a 32 char string that is the same as if I did $iv = substr(hash('sha256', $param2), 0, 32);.

所以我的问题是,我如何在C#中获得与我得到的字节数组相同的结果这行 $ iv = substr(hash('sha256',$ param2),0,16); 的php?

So my question is, how do I get the same byte array result in C# that I get in this line $iv = substr(hash('sha256', $param2), 0, 16); of php? Is this even possible?

推荐答案

好吧,我设法以一种不错的方式解决了这个问题。

Well, I managed to solve this in a not so bad manner.

按照@ ste-fu的建议,我试图摆脱我能找到的每一个编码。

Following @ste-fu advice I tried to get rid of every piece of encoding that I could find.

但是我仍然没有在接近正确使用Key和IV的任何地方。所以我用php做了一些测试。我做了IV的 var_dump ,得到了一个整齐的16长度数组,字节显示为整数。

But I still wasn't anywhere close to getting the Key and IV right. So I did some testing with php. I made a var_dump of the IV and got a neat 16 length array with bytes shown as integers.


var_dump 结果数组始终在[1]中开始。

var_dump result array starts allways in [1]. Be advised.



    $iv = substr(hash('sha256', $param2), 0, 16);
    $byte_array = unpack('C*', $iv);
    var_dump($byte_array);

这使我的兴趣达到了顶峰,认为如果我的十六进制字符串正确,我应该可以将每个字符串中的char等于它的等效字节。瞧,我在C#中完成了此功能:

That peaked my interest, thinking that if I had the hex string right I should be able to convert each char in the string to it's equivalent byte. Lo and behold, I made this function in C#:

    private byte[] StringToByteArray(string hex)
    {
        IList<byte> resultList = new List<byte>();
        foreach (char c in hex)
        {
            resultList.Add(Convert.ToByte(c));
        }
        return resultList.ToArray();
    }

这对于IV来说效果很好。现在,我只需要对密钥执行相同的操作。所以我做了,只是发现我有一个64个长度的字节数组。那很奇怪,但是还可以。在php中进行更多测试。

And this worked very well for the IV. Now I just had to do the same thing for the key. And so I did, just to find that I had a 64 length byte array. That's weird, but ok. More testing in php.

由于php Key的行为与IV相同,因此我没有意识到openssl加密功能如何允许64位长度的Key 。因此,我尝试使用前32个字符组成的密钥对相同的数据进行加密和解密。 $ ky = substr(hash('sha256',$ param1),0,32);
这给了我与完整Key相同的结果。因此,我的有根据的猜测是,openssl仅需要编码所需的字节即可。实际上,自从我测试了长度为1、16、20、32、33和50的子字符串以来,它将花费任何时间。如果字符串的长度大于32,则函数本身会将其剪切。

Since it does make sense that the php Key behaves the same as the IV I didn't get how the openssl encryption functions allowed a 64 length Key. So I tryed to encrypt and decrypt the same data with a Key made from the first 32 chars. $ky = substr(hash('sha256', $param1), 0, 32); And it gave me the same result as with the full Key. So, my educated guess is that openssl just takes the bytes necesary for the encoding to work. In fact it will take anything since I tested with substrings of 1, 16, 20, 32, 33 and 50 length. If the length of the string is bigger than 32 the function itself will cut it.

无论如何,我只需要获取Key hex的前32个字符并使用我的新功能将它们转换为字节数组,我得到了我的钥匙。
因此,现在的主要C#代码如下所示:

Anyway, i just had to get the first 32 chars of the Key hex and use my new function to convert them into a byte array and I got my Key. So, the main C# code right now looks like this:

    public CryptoHelper(string keyFilePath, string ivFilePath)
    {
        //Reading bytes from txt file encoded in UTF8.
        byte[] key = File.ReadAllBytes(keyFilePath);
        byte[] iv = File.ReadAllBytes(ivFilePath);

        IV = StringToByteArray(GetStringHexSha256Hash(iv).Substring(0, 16));
        Key = StringToByteArray(GetStringHexSha256Hash(key).Substring(0, 32)); 

        //Tests
        var st = Encrypt("abcdefg");
        var en = Decrypt(st);
    }


    //Convert each char into a byte
    private byte[] StringToByteArray(string hex)
    {
        IList<byte> resultList = new List<byte>();
        foreach (char c in hex)
        {
            resultList.Add(Convert.ToByte(c));
        }
        return resultList.ToArray();
    }

    private string GetStringHexSha256Hash(byte[] source)
    {
        string result = "";
        try
        {
            using (SHA256 sha256Hash = SHA256.Create("SHA256"))
            {
                //Get rid of Encoding!
                byte[] hashedBytes = sha256Hash.ComputeHash(source);

                for (int i = 0; i < hashedBytes.Length; i++)
                {
                    result = string.Format("{0}{1}",
                                            result,
                                            hashedBytes[i].ToString("x2"));
                }
            }
        }
        catch (Exception)
        {
            throw;
        }

        return result;
    }


    private string Encrypt(string source)
    {
        try
        {
            string result = "";

            using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
            {
                byte[] sourceByteArray = Encoding.UTF8.GetBytes(source);

                using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
                {
                    byte[] encriptedSource = encryptor.TransformFinalBlock(sourceByteArray, 0, sourceByteArray.Length);
                    result = Convert.ToBase64String(encriptedSource);
                    //Nothing to see here, move along.
                    result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
                }
            }

            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

    private string Decrypt(string source)
    {
        try
        {
            string result = "";
            byte[] sourceByte = Convert.FromBase64String(source);
            byte[] sourceFreeOfBase64 = Convert.FromBase64String(Encoding.UTF8.GetString(sourceByte));

            byte[] resultByte;
            int decryptedByteCount = 0;

            using (var aes = new AesManaged { Key = Key, IV = IV, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
            {
                using (ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV))
                {
                    using (MemoryStream memoryStream = new MemoryStream(sourceFreeOfBase64))
                    {
                        using (CryptoStream cs = new CryptoStream(memoryStream, AESDecrypt, CryptoStreamMode.Read))
                        {
                            resultByte = new byte[sourceFreeOfBase64.Length];
                            //Now that everything works as expected I actually get the number of bytes decrypted!
                            decryptedByteCount = cs.Read(resultByte, 0, resultByte.Length);
                        }
                    }
                }
                //Nothing to see here, move along.
                result = Encoding.UTF8.GetString(resultByte);
                //Use that byte count to get the actual data and discard the padding.
                result = result.Substring(0, decryptedByteCount);
            }

            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

我仍然需要清除班级中的所有代码根据我所做的所有测试,但这就是使它工作所需的全部。
我希望这可以帮助遇到我同样问题的任何人。

I still need to clean all the code from my class from all the testing I did, but this is all that's needed to make it work. I hope this helps anybody with the same problem that I faced.

干杯。

这篇关于获取等于php十六进制字符串的C#字节数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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