Search code examples
phpencryptionphpseclibtripledes

Decryption using PBEWithMD5AndTripleDES in PHP


I am working on integrating an API to our web-application. On the initial request, the API returns a response that is encrypted using PBEWithMD5AndTripleDES encryption, and then base 64 encoded. I have an encryption password that is provided to me beforehand. Because of my lack of experience and PBEWithMD5AndTripleDES documentation, I am struggling to decrypt the response. I have tried using phpseclib without any luck.

This is my code with phpseclib

        $res = $response->getBody()->getContents();
        $res = base64_decode($res);
        // this is provided by vendor
        $password = self::PASSWORD;
        // I tried this too.
        //$password = md5(utf8_encode($password), true);

        $tripleDes = new TripleDES(TripleDES::MODE_CBC);
        $tripleDes->setKey($password);
        $ddd = $tripleDes->decrypt($res);

        // this is returning false
        var_dump($ddd); die();

Can you please provide me some examples of how to use PBEWithMD5AndTripleDES in PHP or point me to some direction or documentation.


Solution

  • PBEWithMD5AndTripleDES uses an MD5 based algorithm for key / IV derivation, which expects a password, a salt and an iteration count as parameters. For encryption TripleDES in CBC mode (des-ede3-cbc) with a 24 bytes key is applied.

    PBEWithMD5AndTripleDES is an Oracle proprietary extension of the password-based encryption defined in PKCS#5 (RFC 8018) to support longer keys, here. Because it is proprietary and because of the outdated algorithms like MD5 and the relatively slow TripleDES compared to AES, it should not be used for new implementations, but only for compatibility with legacy code.

    I have not found any PHP library on the web that supports PBEWithMD5AndTripleDES out-of-the-box (only for the different PBEWithMD5AndDES, e.g. here). For a custom implementation you actually only need the derivation of the key / IV. So if you don't find an implementation either, but you have compelling reasons to use this algorithm: Here is a Java code that implements the derivation. A port to PHP could be:

    function deriveKeyIV($key, $salt, $count){
        
        $result = "";
        for ($var = 0; $var < 4; $var++){
            if($salt[$var] != $salt[$var + 4])
            break;
        }
        if ($var == 4){
            for ($var = 0; $var < 2; $var++){
                $tmp = $salt[$var];
                $salt[$var] = $salt[3 - $var];
                $salt[3 - 1] = $tmp;
            }
        }
        for ($var = 0; $var < 2; $var++){
        
            $toBeHashed = substr($salt, $var * (strlen($salt) / 2), strlen($salt) / 2);
            for ($var2 = 0; $var2 < $count; $var2++){
                $toBeHashed = hash ("MD5", $toBeHashed . $key, TRUE);
            }
            $result = $result . $toBeHashed;
        }
        
        return $result;
    }
    

    The function returns 32 bytes, of which the first 24 bytes are the key and the last 8 bytes are the IV. With this key and IV the encryption with TripleDES in CBC mode can then be performed.

    Example:

    $keyIv = deriveKeyIV(hex2bin("01026161afaf0102fce2"), hex2bin("0788fe53cc663f55"), 65536);
    $key = substr($keyIv, 0, 24);
    $iv = substr($keyIv, 24, 8);
    
    print(bin2hex($key) . "\n");
    print(bin2hex($iv) . "\n");
    print(openssl_encrypt("The quick brown fox jumps over the lazy dog", "des-ede3-cbc", $key, 0, $iv));
    

    Output:

    543650085edbbd6c26149c53a57cdd85871fd91c0f6d0be4
    d7ffaa69502309ab
    m4pye0texirKz1OeKqyKRJ5fSgWcpIPEhSok1SBDzgPthsw9XUuoiqXQBPdsVdUr
    

    As reference I used a Java implementation, more precisely the implementation of PBEWithMD5AndTripleDES of the SunJCE provider, which gives the same result.

    Note that the original implementation of PBEWithMD5AndTripleDES only allows a salt that is exactly 8 bytes in size (although the derivation function can handle larger salts), otherwise an exception is thrown (salt must be 8 bytes long). To add this constraint, the following can be added at the beginning of deriveKeyIV:

    if (strlen($salt) != 8) {
        throw new Exception('Salt must be 8 bytes long');
    }