2

I have a common problem but none of the various solutions in the web seems working for me.

I have C# code which make an encrypt 3DES-ECB with PKCS7. I have to do the same in PHP, but I'm getting different results.

This is my C# code:

        public string Encrypt(string toEncrypt, string key)
    {
        byte[] keyArray;
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
        System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
            //Always release the resources and flush data
            // of the Cryptographic service provide. Best Practice
            hashmd5.Clear();            
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

        tdes.Key = keyArray;
        tdes.Mode = CipherMode.ECB;
        tdes.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = tdes.CreateEncryptor();
        byte[] resultArray =
          cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        tdes.Clear();
        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
    }

I've made a lot of tries in php... this is only one of them:

function apiEncode($data)
{    
  $key = "6702BC24DD0527E7";

  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb');
  return base64_encode($encData);
}

I'm using a random key, you can find it in the php sample code.

When the input is "00010", the C# code returns "FcXBCikZU64=" while the php gives to me "FIg+xqod9iY=".

Why? I think I'm doing all the stuff I've found in the blogs/tutorials/etc... so, what's the problem in my case?

UPDATE:

I have add, but still bad news...

$key .= substr($key,0,8);

function apiEncode($data)
{    
  $key = "6702BC24DD0527E7";

  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  $key .= substr($key,0,8); // append the first 8 bytes onto the end

  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb'); //, $iv);
  return base64_encode($encData);
}

now the output is hbJpiCNmXz8=... still not what I need..

UPDATE2: the problem is that, on c# side, I make an hash and I don't know how to do it in php.. look the code "tabbed", is where I do the hash in c# side.. how can I do it in php one?

SOLUTION:

function apiEncode($data)
{    
  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  $key = "6702BC24DD0527E7";
  $key = md5($key,TRUE);
  $key .= substr($key,0,8);
  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb');
  return base64_encode($encData);
}

 $crypt = apiEncode("00010");     
    echo "CRYPT: $crypt";
Piero Alberto
  • 3,823
  • 6
  • 56
  • 108
  • 2
    Since you're using a short key, you probably have to duplicate a part of the key in php. Mcrypt and C# probably expect the two-key 3DES in different ways. How about you try it with a 24 byte key. – Artjom B. Jun 29 '15 at 12:48
  • how can I duplicate the key? do you have a snippet code? I don't have experience with php – Piero Alberto Jun 29 '15 at 12:56
  • 2
    @PieroAlberto See http://php.net/manual/en/function.mcrypt-encrypt.php#47973 *Solving 3DES incompatibilities with .NET's TripleDESCryptoServiceProvider* – xanatos Jun 29 '15 at 13:01
  • 1
    In this specific case (you are using a 128 bits key), try adding a `$key .= substr($key,0,8);` to enlarge the key from 128 to 192 bits. – xanatos Jun 29 '15 at 13:05
  • done.. look the update, it doesn't work.. – Piero Alberto Jun 29 '15 at 13:08
  • 1
    @PieroAlberto The output of the C# on my PC is `hbJpiCNmXz8=`, so the same as the PHP one you wrote. – xanatos Jun 29 '15 at 13:52
  • @xanatos but my C# code gives to me "FcXBCikZU64="... – Piero Alberto Jun 29 '15 at 13:52
  • 1
    @PieroAlberto https://ideone.com/PHvADZ Perhaps you need a new PC :-) – xanatos Jun 29 '15 at 13:53
  • @xanatos I'm sorry, I have put my c# with errors.. please, look it now.. I think it's correct output are different, but why? I have added the code looking "tabbed", so you can find it easy – Piero Alberto Jun 29 '15 at 13:59
  • 1
    @PieroAlberto You are hashing the password in C#, while you aren't doing it in PHP. It is clear why it doesn't work :-) Hashing the password is a good idea, because in this way you can use a password of any length, and it will be translated to a password of 128 bits... Sadly it is a bad idea using MD5 because you need 192 bits of hash :-) – xanatos Jun 29 '15 at 14:01
  • @xanatos how can I get the same result in php? is it possible? – Piero Alberto Jun 29 '15 at 14:03
  • 1
    @PieroAlberto Yes, but I don't program PHP :-) – xanatos Jun 29 '15 at 14:03
  • @xanatos ok, at least we have found the core of the problem... thank you man! :) – Piero Alberto Jun 29 '15 at 14:05

1 Answers1

2

PHP code:

$key = "6702BC24DD0527E7";
$key = md5($key,TRUE);
$key .= substr($key,0,8);

The C# code is "ok" as it is.

"ok" is a big word here. I would probably use SHA256 and trim it to 24 bytes:

C#:

SHA256Managed sha256 = new SHA256Managed();
keyArray = sha256.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
Array.Resize(ref keyArray, 24);
//Always release the resources and flush data
// of the Cryptographic service provide. Best Practice
sha256.Clear(); 

and PHP:

$key = "6702BC24DD0527E7";
$key = hash("sha256",$key,TRUE);
$key = substr($key,0,24);

and still would be "lower case OK"... Normally you should use AES and one of the various block chaining modes, like CBC (that requires a IV), and the password should be "strenghtened" with an algorithm, like PBKDF2 (requires PHP >= 5.5)

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • THIS is what I needed!! Thank you!! – Piero Alberto Jun 29 '15 at 14:13
  • 1
    @Piero Better not use 3DES or ECB mode. – Artjom B. Jun 29 '15 at 14:15
  • @ArtjomB. There are various levels of OK :-) Yep you are right. AES + CBC is OK – xanatos Jun 29 '15 at 14:16
  • @ArtjomB. Still, 3des is secure from what I know... the only problem would be ECB. – xanatos Jun 29 '15 at 14:17
  • And AES+CBC + HMAC even better – Artjom B. Jun 29 '15 at 14:18
  • @ArtjomB.why is 3DES + ECB a problem? – Piero Alberto Jun 29 '15 at 14:18
  • @ArtjomB. I normally prefer PBKDF2 – xanatos Jun 29 '15 at 14:19
  • 2
    @PieroAlberto 3DES is old and slow, and in the end it uses an equivalent key length of 112 bits. AES is newer (but not too much new, something important in cryptography), faster, and is the current "standard". The ECB has the problem that it doesn't have a IV, so if you encrypt twice the same text, you'll get the same ciphered output, *and* each block of data is encrypted independently from the others, so if you encrypt in the same "session" data that has repetitions, these repetitions are evident – xanatos Jun 29 '15 at 14:22
  • I meant HMAC for authenticating the ciphertexts (encrypt-then-MAC) to prevent attacks such as padding oracle attack. PBKDF2 for key derivation is the least one should do. – Artjom B. Jun 29 '15 at 14:30
  • @PieroAlberto As a sidenote, PHP has a "funny"/"complex" handling of encoding... When you'll deploy your app, you should check that the encoding is handled correctly with passwords that contain non-ASCII characters, like `àèéìòù` – xanatos Jun 29 '15 at 14:34