将PHP解密AES-256-CBC移植到C#

时间:2019-01-06 02:05:41

标签: c# php encryption

我有以下PHP解密例程,该例程运行正常,需要帮助将其转换为c#。我尝试了很多方法,但是都没有用。 我设法匹配C#和PHP之间的哈希函数输出。 还匹配了与base64之间的转换输出。

PHP代码:

function decrypt($encrypted_txt, $secret_key, $secret_iv)
{
$encrypt_method = "AES-256-CBC";
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output = openssl_decrypt(base64_decode($encrypted_txt), $encrypt_method, $key, 0, $iv);
return $output;
}
secret_key= "t-3zafRa"; 
secret_key_hash = "d03a4d94b29e7f55c80726f1152dcebc9f03f4c698470f72083af967cf786b6b";
     

问题在于密钥哈希是64个字节,对于AES-256无效,但我不确定它在php中如何工作以及openssl_decrypt php函数如何处理密钥。

我也曾尝试传递密钥哈希的MD5,但也未能解密。

byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(keyhash);
byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes);
string keymd5 = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); //To match with PHP MD5 output

C#哈希函数:

static string sha256(string randomString)
        {
            var crypt = new System.Security.Cryptography.SHA256Managed();
            var hash = new System.Text.StringBuilder();
            byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
            foreach (byte theByte in crypto)
            {
                hash.Append(theByte.ToString("x2"));
            }
            return hash.ToString();
        }

C#解密例程:

static string DecryptStringFromBytesAes(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("iv");

            // Declare the RijndaelManaged object
            // used to decrypt the data.
            RijndaelManaged aesAlg = null;

            // Declare the string used to hold
            // the decrypted text.
            string plaintext;

            // Create a RijndaelManaged object
            // with the specified key and IV.
           aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 256, BlockSize = 128, Key = key, IV = iv };

            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                        srDecrypt.Close();
                    }
                }
            }

            return plaintext;
        }

任何帮助或想法都将受到高度赞赏。

1 个答案:

答案 0 :(得分:3)

openssl_decrypt只需为密钥占用算法所需的字节数即可。由于您的算法是“ AES-256-CBC”,因此使用32字节(256位),因为AES-256被定义为具有256位密钥(并且是14轮,而不是10或12轮)的AES。

PHP执行此操作的方式是在密钥太小的情况下在右边添加00个有价值的字节,或者(如您所愿)仅通过忽略第32个字节之后的字节。这不是表现任何类型的密码库的好方法,尤其是对于像PHP这样的高级语言而言,但是OpenSSL包装器库仍然可以做到这一点。

因此,您必须从十六进制编码的密钥中提取前32个字节,并将其用作C#中的密钥以兼容。使用不同的哈希函数当然是行不通的,MD5和SHA-256完全不兼容(根据设计)。当然,您现在还有16个十六进制编码的字节,这意味着您将使用具有128位密钥的AES-256,从而具有128位安全性。是的,您需要在C#中使用PKCS#7填充。


请注意,使用带有静态IV的CBC是不安全的。将CBC模式用于传输模式安全性并不安全。使用SHA-256或几乎没有熵的密码或密钥使用任何普通哈希是不安全的。通常,将密钥存储在字符串中并不安全。

使加密工作足够困难;确保其安全性要困难得多,并且首先需要了解您的操作。您需要从针对特定用例的良好协议开始(这已经跳过了几个步骤)。