问题
I'm trying to port this C# code to 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.
This is the PHP code I put together:
$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. The result from PHP is some 32 character long gibberish - I guess binary data.
Can anybody help me to fix that code, or has an idea what I can try?
回答1:
As mentioned by zaph in the comments this code is not considered safe and i advise against using it in a production environment. Quoting zaph's comment:
Essentially the code is not secure. Keys should be created from a password with a key derivation function such as PBKDF2. IVs should be random for each encryption, never the password/key. The IV can be and generally are prepended to the encrypted data, they do not need to be secret.
That being said, here is a PHP equivalent of your C# code:
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. In order to decode them properly in PHP we'll have to use mb_convert_encoding.
PHP test:
$password = "012345";
$content = "EJWgZ/26wp+Cb5utbM1aMk8XfqPQide4dzjQzzzYfj8=";
echo DecryptString($content, $password);
//0123456789
C# test:
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. We can take advantage of those features by setting the options
parameter to false
.
IVs should be random bytes. This is important because using a static IV makes your encryption vulnerable to attacks. You can create secure random IVs with RNGCryptoServiceProvider
for C#, and openssl_random_pseudo_bytes
for PHP.
Passwords should be as long and unpredictable as possible - 123456 is not a good password! 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. You can use Rfc2898DeriveBytes
for C#, and hash_pbkdf2
for 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. You can use a MAC to verify your message (HMACSHA256
for C#, hash_hmac
for PHP) or use an authenticated mode like GCM.
来源:https://stackoverflow.com/questions/46362229/porting-c-sharp-aes-256-decryption-to-php