简体   繁体   English

将C#AES 256解密移植到PHP

[英]Porting C# AES 256 decryption to PHP

I'm trying to port this C# code to PHP: 我正在尝试将此C#代码移植到PHP:

private static string DecryptString(string content, string password)
{
    Rijndael aes;
    byte[] retVal = null;
    byte[] contentBytes;
    byte[] passwordBytes;
    byte[] ivBytes;

    try {
        //Get the content as byte[]
        contentBytes = Convert.FromBase64String(content);

        //Create the password and initial vector bytes
        passwordBytes = new byte[32];
        ivBytes = new byte[16];

        Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length);
        Array.Copy(passwordBytes, ivBytes, 16);

        //Create the cryptograpy object
        aes = Rijndael.Create();
        aes.Key = passwordBytes;
        aes.IV = ivBytes;
        aes.Padding = PaddingMode.PKCS7;

        string mode = aes.Mode.ToString();

        //Decrypt
        retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length);
    }
    catch {}

    return Encoding.Unicode.GetString(retVal);
}

The content parameter is a 44 character long string, base 64 encoded, the password parameter is a 6 character long string. content参数是一个44字符长的字符串,以64为基数编码, password参数是一个6字符长的字符串。

This is the PHP code I put together: 这是我放在一起的PHP代码:

$content = "[44 char base 64 encoded string]";
$password = "[6 char password]";

$padding = 32 - (strlen($password) % 32);
$password .= str_repeat(chr($padding), $padding);

$iv = substr($password, 0, 16);

$data = base64_decode($content);

$decrypted  = openssl_decrypt($data, 'AES-256-CBC', $password, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);

The result of the C# code is a 10 character long number. C#代码的结果是10个字符长的数字。 The result from PHP is some 32 character long gibberish - I guess binary data. PHP的结果是32个字符长的乱码-我猜是二进制数据。

Can anybody help me to fix that code, or has an idea what I can try? 有人可以帮助我修复该代码,或者对我可以尝试的方法有所了解吗?

As mentioned by zaph in the comments this code is not considered safe and i advise against using it in a production environment. 正如zaph在评论中所提到的,此代码不被认为是安全的,我建议不要在生产环境中使用它。 Quoting zaph's comment : 引用zaph的评论

Essentially the code is not secure. 本质上,代码是不安全的。 Keys should be created from a password with a key derivation function such as PBKDF2. 应该使用具有密钥派生功能的密码(例如PBKDF2)来创建密钥。 IVs should be random for each encryption, never the password/key. 每次加密的IV应该是随机的,而不是密码/密钥。 The IV can be and generally are prepended to the encrypted data, they do not need to be secret. IV可以并且通常是加密数据的前缀,它们不需要是秘密的。

That being said, here is a PHP equivalent of your C# code: 话虽如此,这是C#代码的PHP等效项:

function DecryptString($content, $password){
    $password = mb_convert_encoding($password, "utf-16le");
    $padding = 32 - (strlen($password) % 32);
    $password .= str_repeat("\0", $padding);
    $iv = substr($password, 0, 16);
    $decrypted = openssl_decrypt($content, "AES-256-CBC", $password, false, $iv);
    $decoded = mb_convert_encoding($decrypted, "utf-8", "utf-16le");
    return $decoded;
}

C# Unicode strings are Little Endian UTF-16 encoded. C#Unicode字符串是Little Endian UTF-16编码的。 In order to decode them properly in PHP we'll have to use mb_convert_encoding . 为了在PHP中正确解码它们,我们必须使用mb_convert_encoding

PHP test: PHP测试:

$password = "012345";
$content = "EJWgZ/26wp+Cb5utbM1aMk8XfqPQide4dzjQzzzYfj8=";
echo DecryptString($content, $password);

//0123456789

C# test: C#测试:

string password = "012345";
string content = "EJWgZ/26wp+Cb5utbM1aMk8XfqPQide4dzjQzzzYfj8=";
Console.WriteLine(so.DecryptString(content, password));

//0123456789

Some tips: 一些技巧:

PHP's openssl_decrypt uses PKCS padding by default, and can handle base64 encoded data. PHP的openssl_decrypt默认情况下使用PKCS填充,并且可以处理base64编码的数据。 We can take advantage of those features by setting the options parameter to false . 我们可以通过将options参数设置为false来利用这些功能。

IVs should be random bytes. IV应该是随机字节。 This is important because using a static IV makes your encryption vulnerable to attacks. 这很重要,因为使用静态IV会使您的加密容易受到攻击。 You can create secure random IVs with RNGCryptoServiceProvider for C#, and openssl_random_pseudo_bytes for PHP. 您可以使用C#的RNGCryptoServiceProvider创建安全的随机IV,而使用PHP的则创建openssl_random_pseudo_bytes

Passwords should be as long and unpredictable as possible - 123456 is not a good password! 密码应尽可能长且不可预测-123456不是一个好的密码! Although you could use your password as a key (if it has the right size), it's best to use a key created with a KDF. 尽管您可以将密码用作密钥(如果密码大小合适),但是最好使用由KDF创建的密钥。 You can use Rfc2898DeriveBytes for C#, and hash_pbkdf2 for PHP. 您可以将Rfc2898DeriveBytes用于C#,并将hash_pbkdf2用于PHP。

If you don't check the authenticity of the message, then your data could be altered, or your service could be vulnerable to padding oracle attacks. 如果您不检查消息的真实性,那么您的数据可能会被更改,或者您的服务可能会受到填充oracle攻击的攻击。 You can use a MAC to verify your message ( HMACSHA256 for C#, hash_hmac for PHP) or use an authenticated mode like GCM. 您可以使用MAC来验证您的邮件( HMACSHA256为C#, hash_hmac对于PHP),或者使用经过验证的模式,像GCM。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM