【问题标题】:Porting PHP decryption AES-256-CBC to C#将 PHP 解密 AES-256-CBC 移植到 C#
【发布时间】:2019-01-06 02:05:41
【问题描述】:

我有以下 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;
        }

非常感谢任何帮助或想法。

【问题讨论】:

  • 为什么你认为key是64字节?对我来说,它看起来像 32。
  • 请注意,十六进制编码每个字节使用 两个 个字符,因此如果您将 32 字节的密钥编码为十六进制,它将产生一个 64 个字符的字符串。
  • 但是,您的填充不正确。 openssl_decrypt 默认使用 PKCS#7-padding,但在 C# 中,您完全禁用了填充。您需要设置Padding = PaddingMode.PKCS7

标签: c# php encryption


【解决方案1】:

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 个十六进制编码字节,这意味着您正在使用 AES-256 和 128 位密钥,从而为您提供 128 位安全性。是的,您需要在 C# 中使用 PKCS#7 填充。


请注意,将 CBC 与静态 IV 结合使用并不安全。将 CBC 模式用于传输模式安全性并不安全。使用 SHA-256 或任何普通散列对密码或熵很少的密钥是不安全的。将密钥存储在字符串中通常是不安全的。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    相关资源
    最近更新 更多