PHP等效的.net AES加密

本文关键字:AES 加密 net PHP | 更新日期: 2023-09-27 18:15:32

我正在与我的客户端进行数据交换集成,他们发送给我的数据使用他们的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;
    }
}
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"
    );
}

PHP等效的.net AES加密

有人能发现这个错误吗?

是的,大的不是你的错:mcrypt令人困惑的API又来了。

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

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中,都应该考虑部署一个先加密后认证的协议。请参阅这篇关于加密和身份验证的博客文章。此外,您编写的所有加密代码都可能被破坏。

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