views:

114

answers:

3

I've inherited some C# code and need to port it to PHP. Here it is:

string key = "some key";
string strEncrypted = "some encrypted string";

byte[] hashedKey = new MD5CryptoServiceProvider().ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
byte[] strToDecrypt = Convert.FromBase64String(strEncrypted);

TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.Key = hashedKey;
tripleDES.Mode = CipherMode.ECB;

string strDecrypted = UTF8Encoding.UTF8.GetString(tripleDES.CreateDecryptor().TransformFinalBlock(strToDecrypt, 0, strToDecrypt.Length));

My PHP code looks like this:

$key = 'some key';
$str_encrypted = 'some encrypted string';

$hashed_key = md5($key, TRUE);
$str_to_decrypt = base64_decode($str_encrypted);

// The IV isn't used for ECB, but it prevents a warning.
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB), MCRYPT_RAND); 

$str_decrypted = mcrypt_decrypt(MCRYPT_TRIPLEDES, $hashed_key, $str_to_decrypt, MCRYPT_MODE_ECB, $iv);

But the two decrypted values are not the same, and I can't figure out why. I've read a lot of similar questions here and elsewhere, but none of them seem to explain the issue I'm having.

I'd really appreciate any help in figuring out why the decrypted PHP string doesn't match the decrypted C# string.

A: 

The C# key is encoded in UTF8 before being hashed with MD5. PHP equivalent probably isnt?

The C# output is also UTF8 encoded again.

I'm not too familiar with PHP, but apparently the function you want is utf8_encode, so try updating your PHP side to this:

$key = 'some key';
$str_encrypted = 'some encrypted string';

$hashed_key = md5(utf8_encode($key), TRUE);
$str_to_decrypt = base64_decode($str_encrypted);

// The IV isn't used for ECB, but it prevents a warning.
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB), MCRYPT_RAND); 

$str_decrypted = utf8_encode(mcrypt_decrypt(MCRYPT_TRIPLEDES, $hashed_key, $str_to_decrypt, MCRYPT_MODE_ECB, $iv))

Beyond the UTF8 encoding difference, its not guaranteed (from the samples you provided) that str_encrypted is base64 encoded, on either the c# or php side.

You'd be best taking colithium's advice and echoing out the inputs to ensure that all your input data matches at a byte level.

PaulG
Yeah, I tried that, but it doesn't affect the output.
matthew
Its almost always an encoding error at one side. Is the str_encrypted definately base64 encoded on both sides?
PaulG
Yeah I'm using the exact same string, as shown in my example code. Same input.
matthew
+2  A: 

You might want to review this forum: http://forums.asp.net/t/1498290.aspx Apparently someone last year had what appears to be the exact same problem.

From that site it looks like the C# stuff should be UTF-7 encoded.. Not UTF-8.

Chris Lively
I had seen that, but it seemed as though the utf7 thing was a misunderstanding of how it was working. (In either case, it doesn't seem to work.)
matthew
+1  A: 

I finally managed to find the answer in a comment on the Mcrypt page of the PHP manual:

When using 3DES between PHP and C#, it is to be noted that there are subtle differences that if not strictly observed, will result in annoying problem encrypt/decrypt data.

1), When using a 16 bytes key, php and c# generates total different outcome string. it seems that a 24 bytes key is required for php and c# to work alike.

2), php doesnt have a "padding" option, while c# has 3 (?). My work around is to add nulls i.e. chr(0) to the end of the source string to make its size times of 8, while in c#, PaddingMode.Zeros is required.

3) the key size has to be times of 8, in php, to make it work for c#.

The key point here is #1. Since the key used is the result of md5 hashing, it'll be 16 bytes. If instead I use a 24-byte key to encrypt (and then decrypt) everything is hunky-dory.

So far, I haven't been able to find an explanation of why 16-byte keys produce a different result (or whether there's a workaround). If you've got any info about that, please share!

matthew