4

I know 3DES and MD5 are insecure. I will work on replacing them once I have it working again,

I have a mobile app that is using 3DES with an MD5 of a key as a SECRET KEY to talk to a PHP Application.

Now this code worked perfectly on PHP 5.3 (this is an example I have generated)

mcrypt_decrypt(
    MCRYPT_3DES, 
    md5(
        utf8_encode(
            "MobileAppSecureKey"
        ),
        true
    ), 
    base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI="), 
    MCRYPT_MODE_CBC, 
    base64_decode("cTOCJ/iYL18=")
)

Now I have ported it over to use OpenSSL method my new code is

openssl_decrypt(
    base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI="), 
    'DES-EDE3-CBC', 
    md5(
        utf8_encode(
            "MobileAppSecureKey"
        ),
        true
    ), 
    0, 
    base64_decode("cTOCJ/iYL18=")
)

But the new code does not work, It gives the error error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length from openssl_error_string()

I'm unsure why it's complaining about final block length when this worked perfectly in mcrypt

The code used to generate the test data was from the mobile the mobile app is a Cordova app and uses the CryptoJS library

key = CryptoJS.MD5(key);

// copy 3DES subkey 1 to the last 64 bit to make a full 192-bit key
key.words[4] = key.words[0];
key.words[5] = key.words[1];

if(typeof(iv) === "undefined"){
    iv = CryptoJS.lib.WordArray.random(8);
}

var encrypted = CryptoJS.TripleDES.encrypt(pt, key, {iv: iv});
return {"str":encrypted.toString(), "iv":CryptoJS.enc.Base64.stringify(iv)};

The Encrypted payload was

{"jsonrpc":"2.0","method":"events.enableParentGenres","params":[159],"id":1}

Modifications that have been tried,

As per yivi suggestion, options have been set to OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING

As per Tuckbros suggestion, the message has been padded

$message_padded = base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI=");
$message_padded = str_pad($message_padded,
        strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");

Both of these prevented the error about final block length however the encrypted payload when running through the code did not decrypt.

Barkermn01
  • 6,781
  • 33
  • 83
  • No the Mobile App is encrypting it using a JS library (CryptoJS) it's a Cordova Mobile App (updating to show code) – Barkermn01 Mar 01 '19 at 16:17
  • not the same as here ? https://stackoverflow.com/questions/41181905/php-mcrypt-encrypt-to-openssl-encrypt-and-openssl-zero-padding-problems – Tuckbros Mar 01 '19 at 16:23
  • @Tuckbros no I have just tried adding the padding and that does not work the message when decrypted using that method is the wrong bytes – Barkermn01 Mar 01 '19 at 16:38
  • Are you sure the `mcrypt_decrypt` example is valid? because key size for `DES` with `CBC` is 24 bytes and PHP [`md5`](http://php.net/manual/en/function.md5.php) return a 16 bytes length hash. Your example produces a warning with no result *"Key of size 16 not supported by this algorithm. Only keys of size 24 supported..."*. – Christos Lytras Mar 07 '19 at 11:16
  • @ChristosLytras Yeah I'm sure it was working perfectly on PHP 5, and MD5 is a 32 Byte hash, `md5("test") = "098F6BCD4621D373CADE4E832627B4F6"` 32 Chars and each char is 1 Byte – Barkermn01 Mar 07 '19 at 11:17
  • You also wrong about Keylength in 3DES it must be 16 or 24, "however, the key itself, as encoded into bytes, stored and exchanged, must have length 16 or 24 bytes" from https://stackoverflow.com/questions/5632211/3des-key-size-matter-in-c-net – Barkermn01 Mar 07 '19 at 11:25
  • @MartinBarker check your code running in PHP 5.6 http://sandbox.onlinephpfunctions.com/code/a27f3c4cb311a631c786af51f6b71422a0745605 – Christos Lytras Mar 07 '19 at 11:27
  • Yeah run it as PHP 5.3 – Barkermn01 Mar 07 '19 at 11:28
  • 1
    Yes, I see it now. When we talk about key size we are talking about the bytes length, not the encoded string length. Maybe you should update your question that the PHP code is for PHP 5.3 because it seems `mcrypt` for PHP > 5.3 only supports 192 bit keys for DES CBC (*3 key version*). – Christos Lytras Mar 07 '19 at 11:37
  • It's getting to the point that im gonna have to scrap the entire encryption system and rebuild from the ground up. – Barkermn01 Mar 07 '19 at 11:41
  • 2
    @MartinBarker you won't have to. The solution is that you need to convert the 128 bits key you have to a 192 bits key like in the javascript code (*concat the key with its first 64 bits*). Also if you don't want to use the option `OPENSSL_RAW_DATA`, then pass the base64 encoded string directly without using `base64_decode` to decode it. – Christos Lytras Mar 07 '19 at 18:10

1 Answers1

4

The parameters you are passing in openssl_decrypt seem to be wrong; you are passing the OPTIONS parameter as 0, which you have to set to OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING because you provide the function with raw data (base64_decode).

The key also needs to be converted to a 192 bits key, as in the javascript code:

$key = md5(utf8_encode("MobileAppSecureKey"), true);

//key.words[4] = key.words[0];
//key.words[5] = key.words[1];

for($i = 0; $i < 8; $i++) {
    $key[$i + 16] = $key[$i];
}

Try this one out:

$key = md5(utf8_encode("MobileAppSecureKey"), true);
$data = base64_decode("bkCfcseIt/TPsgNCdyX9fv2/4MjOJdaPXakNNbxQT3n6tXHa5bDoXojQ3g7jPLCu+wjwD0guQzw3hCFUSVx47PmDNHASk7g/kJ4K4tX0VGI=");

for($i = 0; $i < 8; $i++) {
    $key[$i + 16] = $key[$i];
}

$decoded = openssl_decrypt(
    $data,
    'DES-EDE3-CBC',
    $key,
    OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
    base64_decode("cTOCJ/iYL18=")
);

echo "\$decoded = {$decoded}";

// Will output:
// $decoded = {"jsonrpc":"2.0","method":"events.enableParentGenres","params":[159],"id":1}
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113