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

将PHP解密AES-256-CBC移植到C#,c#,php,encryption,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 m

我有下面的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;
}
问题是密钥散列是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;
        }
静态字符串解密StringFromByteSAES(字节[]密文,字节[]密钥,字节[]iv)
{
//检查参数。

if(cipherText==null | | cipherText.Length
openssl_decrypt
只需要算法所需的密钥字节数。由于您的算法是“AES-256-CBC”,它使用32个字节(256位),因为AES-256被定义为具有256位密钥的AES(和14轮,而不是10或12轮)

PHP实现这一点的方法要么是在右边添加
00
值字节,以防密钥太小,要么像您的情况一样,简单地忽略第32个字节之后的字节。这对于任何类型的加密库来说都不是一种好方法,特别是对于像PHP这样的高级语言,但OpenSSL包装库可以做到这一点对

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


请注意,将CBC与静态IV一起使用是不安全的。将CBC模式用于传输模式安全性是不安全的。对密码或密钥使用SHA-256或任何具有较小熵的普通哈希是不安全的。将密钥存储在字符串中通常是不安全的


让加密工作起来已经够难了;让它安全起来要困难得多,首先需要了解你在做什么。你需要为你的特定用例制定一个好的协议(这已经跳过了几个步骤).

为什么您认为密钥是64字节?我觉得它像32字节。请注意,十六进制编码每字节使用两个字符,因此如果您将32字节的密钥编码为十六进制,则会产生64个字符的字符串。但是,您的填充是不正确的。
openssl\u decrypt
默认情况下使用PKCS#7-填充,但在C中,您完全禁用了填充。Y您需要设置
Padding=PaddingMode.PKCS7
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;
        }