0

I'm trying to port the following C# method to PHP but I can't get the output right.

public static string Encrypt()
{
      String text = "123456";
      String key = "1r1ppl3x";
      byte[] arrText = UTF8Encoding.UTF8.GetBytes(text);
      TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
      tdes.Key = (new UnicodeEncoding()).GetBytes(key);
      tdes.Mode = CipherMode.ECB;
      tdes.Padding = PaddingMode.PKCS7;
      ICryptoTransform cTransform = tdes.CreateEncryptor();
      byte[] resultArray = cTransform.TransformFinalBlock(arrText, 0, arrText.Length);
      tdes.Clear();
      String encrypted = Convert.ToBase64String(resultArray, 0, resultArray.Length);
      return encrypted;


}

My attempt in PHP so far :

function encrypt()
{
    $key = utf8_encode("1r1ppl3x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
    $data = utf8_encode("123456"); 
    $blocksize = mcrypt_get_block_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB);
    $tripleKey = substr($key, 0, mcrypt_get_key_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB));
    $paddingSize = $blocksize - (strlen($data) % $blocksize); 
    $data .= str_repeat(chr($paddingSize), $paddingSize);
    $encodedText = mcrypt_encrypt(MCRYPT_TRIPLEDES, $tripleKey, $data, MCRYPT_MODE_ECB);
    return base64_encode($encodedText);
}

for some reason I can't get the output of the PHP code the same as the one in C#.

Also, I know mcrypt_get_block_size and other mcrypt related methods are deprecated in PHP 7.1 and it is not advisable to use it.

xar
  • 1,429
  • 2
  • 17
  • 29
  • Why are you using Triple DES with a 8-byte key? That just reverts to DES which is not secure. ]ECB mode is not secure, see [ECB mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29), scroll down to the Penguin. – zaph Oct 18 '17 at 11:56
  • It is best not to use PHP mcrypt, it is abandonware, has not been updated in years and does not support standard PKCS#7 (née PKCS#5) padding, only **non-standard null padding** that can't even be used with binary data. mcrypt has many outstanding [bugs](https://sourceforge.net/p/mcrypt/bugs/) dating back to 2003. The mcrypt-extension is deprecated will be removed in PHP 7.2. Instead consider using [defuse](https://github.com/defuse/php-encryption) or [RNCryptor](https://github.com/RNCryptor), they provide a complete solution and are being maintained and is correct. – zaph Oct 18 '17 at 11:56
  • I appreciate the advice but I can't change the C# code because it's not mine. It's part of a legacy software of our client. I am tasked to make a PHP equivalent of the existing C# code. – xar Oct 18 '17 at 12:00
  • @zaph thanks will take a look at it. – xar Oct 18 '17 at 12:01
  • There is a choice to be made, is the security important? – zaph Oct 18 '17 at 12:07

2 Answers2

2
tdes.Key = (new UnicodeEncoding()).GetBytes(key);

That UnicodeEncoding constructor is for UTF-16, little-endian, "with BOM". The "with BOM" doesn't matter because nothing called GetPreamble(), so for all intents and purposes it's UTF-16LE.

This means your key ("1r1ppl3x") is actually:

31 00 72 00 31 00 70 00 70 00 6C 00 33 00 78 00

Which is a 2DEA key with

  • k1 = { 31 00 72 00 31 00 70 00 }
  • k2 = { 70 00 6C 00 33 00 78 00 }

Which is illegal. Except .NET, like everything else, doesn't care about DES parity bits, so it just pretends that the least significant bit of each byte was correct. Whatever library in PHP will probably do the same. If not, you might want to correct the parity manually.

A 2DEA key has an equivalent 3DEA key of k3=k1. So if your PHP library doesn't support 2DEA keys you can artificially expand it (after correctly using UTF-16LE instead of UTF-8) by copying the first 8 bytes to the end, building a 24-byte value.

If utf8_encode is the best "turn text into binary data" in PHP then you want to use the input string "1\0r\01\0p\0p\0l\03\0x\01\0r\01\0p\0".

It looks like you've already found How to add/remove PKCS7 padding from an AES encrypted string?, so correcting the key should put you into an "equivalent functionality" state.

Now all your problems are that you're using a 56-bit key (all those zeros are pretty easy to guess) as if it were a 168-bit key, and using ECB mode, and using a deprecated library, and (arguably) that you're using 3DES instead of AES, and (personal opinion) moving from a strongly-typed language to a scripting language.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
0
  1. The C# code is using PKCS#7 padding, the PHP MCRYPT code is using null padding.

  2. There is an assumption that the key should be extended with nulls.

  3. There is no point in using Triple DES with a single DES key of 8-bytes. Further only 56-bits of an 8-bit DES key are used. a 56-bit key is not considered secure.

zaph
  • 111,848
  • 21
  • 189
  • 228