【问题标题】:PHP equivalent of .net AES encryptionPHP 等效于 .net AES 加密
【发布时间】:2015-12-29 07:24:03
【问题描述】:

我正在与我的客户进行数据交换集成,他们发送给我的数据使用他们的 C#encrypt 方法(如下)加密。

我的应用程序正在运行 PHP 5.3,我需要一个等效的代码来解密它们发送的数据。我有 PHP 代码,但它不能为我正确解密客户端数据。

很明显,我在加密/解密方法、IV 密钥等方面犯了一些错误。任何人都可以发现错误吗?

谢谢。

C# 代码(来自我的客户):

using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

public class Program
{
    public static void Main()
    {
        var text = "this is a plain string";
        var enc = Program.Encrypt(text);
        Console.WriteLine(enc);
        Console.WriteLine(Program.Decrypt(enc));
    }

    public static string Encrypt(string clearText)
    {
        var EncryptionKey = "1234567890123456";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            byte[] IV = new byte[15];
            var rand = new Random();
            rand.NextBytes(IV);
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }

                clearText = Convert.ToBase64String(IV) + Convert.ToBase64String(ms.ToArray());
            }
        }

        return clearText;
    }

    public static string Decrypt(string cipherText)
    {
        var EncryptionKey = "1234567890123456";
        byte[] IV = Convert.FromBase64String(cipherText.Substring(0, 20));
        cipherText = cipherText.Substring(20).Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }

                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }

        return cipherText;
    }
}

我拥有的 PHP 代码:

public function encrypt($plainText)
{
    $secretKey = '1234567890123456';

    return rtrim(
        base64_encode(
            mcrypt_encrypt(
                MCRYPT_RIJNDAEL_256,
                $secretKey, $plainText,
                MCRYPT_MODE_ECB,
                mcrypt_create_iv(
                    mcrypt_get_iv_size(
                        MCRYPT_RIJNDAEL_256,
                        MCRYPT_MODE_ECB
                    ),
                    MCRYPT_RAND)
            )
        ), "\0"
    );
}

public function decrypt($encodedData)
{
    $secretKey = '1234567890123456';

    return rtrim(
        mcrypt_decrypt(
            MCRYPT_RIJNDAEL_256,
            $secretKey,
            base64_decode($encodedData),
            MCRYPT_MODE_ECB,
            mcrypt_create_iv(
                mcrypt_get_iv_size(
                    MCRYPT_RIJNDAEL_256,
                    MCRYPT_MODE_ECB
                ),
                MCRYPT_RAND
            )
        ), "\0"
    );
}

【问题讨论】:

    标签: c# php encryption aes encryption-symmetric


    【解决方案1】:

    谁能发现错误?

    是的,大的不是你的错:mcrypt's confusing API strikes again

    也就是说,这里实际上有多个错误。

    return rtrim( // unnecessary
        base64_encode(
            mcrypt_encrypt(
                MCRYPT_RIJNDAEL_256, // Not AES
                $secretKey, $plainText,
                MCRYPT_MODE_ECB, // BAD, use MCRYPT_MODE_CBC or 'ctr' instead
                mcrypt_create_iv(
                    mcrypt_get_iv_size(      // unless you're going make this
                        MCRYPT_RIJNDAEL_256, // configurable, you should just
                        MCRYPT_MODE_ECB      // hard-code this as an integer
                    ),
                    MCRYPT_RAND) // BAD, use MCRYPT_DEV_URANDOM
            )
        ), "\0"
    ); 
    

    如果您要生成 IV,则应该传达它,以便您的接收者可以成功解密相同的第一个块。 C# 代码会这样做,而 PHP 不会。

    从密码学工程的角度来看,您应该考虑在 C# 领域和 PHP 中部署先加密然后身份验证协议。见this blog post on encryption and authentication。另外,all the crypto code you've ever written is probably broken

    【讨论】:

      【解决方案2】:

      PHP 脚本似乎使用了错误的模式:
      https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.mode%28v=vs.110%29.aspx
      C# 函数不设置任何模式,因此默认为 CBC。
      PHP部分改用ECB,不仅错误,而且不安全。

      【讨论】:

      • 这不是唯一的事情。 OP 在 C# 中使用 Rijndael-128,在 PHP 中使用 Rijndael-256。 OP 不会在 PHP 中派生密钥和 IV。 OP 还在 PHP 代码中的加密 和解密 中使用随机 IV。 (请随时将此添加到您的答案中。)
      • 刚刚编辑了帖子以使其更加具体。还对 Artjom B 和 EaranMaleasi 建议的代码进行了一些更正(非常感谢)。代码仍然不起作用。还有什么建议吗?与此同时,我会继续自己寻找。
      • @AtherHashmi 我回滚了您的编辑。请不要在收到答复后更改您的问题。您的更改使答案无效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-07
      • 2014-03-24
      • 2021-08-14
      • 2014-05-08
      • 2021-11-23
      • 1970-01-01
      相关资源
      最近更新 更多