Search code examples
javascriptphpaesphpseclibpidcrypt

How to AES CBC encrypt using pidCrypt, then decrypt with phpseclib?


I need to encrypt a short string using Javascript (pidCrypt AES CBC) then decrypt it using PHP (phpseclib Crypt_AES CBC).

There are so many incompatibilities between various libraries (tried many of them, some I couldn't even start to use because of lack of documentation) that I am way over my head trying to understand and overcome them (the current "standards" just don't cover debugging, and gibberish or simply a false will be returned with incorrect usage).

pidCrypt AES CBC says it is using OpenSSL compatible encoding.

So far, I can't understand why the output from pidCrypt won't be decrypted in phpseclib:

pidCrypt

var aes=new pidCrypt.AES.CBC();
aes.encryptText("abcdefg", "secret", {nBits: 128});

console.log(aes.pidcrypt.getParams());
/**
A0_PAD: true
UTF8: true
blockSize: 16
clear: true
dataIn: "abcdefg"
dataOut: "U2FsdGVkX19p8dJctoZSgamat+UBHMWM/Zx64fBDYJY="
decryptIn: ""
decryptOut: ""
encryptIn: Array[16]
encryptOut: "©·åÅýzáðC`"
iv: "e314f8ebbcc5a1e0ae6c27033dd6725f"
key: "49f9bd99b7952b68b3bfa47404455fa7"
nBits: 128
salt: "69f1d25cb6865281"
*/

phpseclib

function hex2raw($strHexString)
{
    $strRawBytes="";
    $arrChunks=str_split($strHexString, 2);
    for($i=0; $i<count($arrChunks); $i++)
        $strRawBytes.=chr(hexdec($arrChunks[$i]));
    return $strRawBytes;
}

require_once("Crypt/AES.php");

$aes=new Crypt_AES(CRYPT_AES_MODE_CBC);

$aes->setPassword("secret");
$aes->setIV(hex2raw("e314f8ebbcc5a1e0ae6c27033dd6725f"));

var_export($aes->decrypt(base64_decode("U2FsdGVkX19p8dJctoZSgamat+UBHMWM/Zx64fBDYJY=")));
/**
false
*/

I have also tried the phpseclib AES CBC mode "Key Derivation: None" example (setKey and setIV instead of setPassword), with the same results.

How can I get phpseclib to decode the above pidCrypt AES CBC output?

or

Are there alternatives to phpseclib which can decrypt the above pidCrypt output (which have a code API; OpenSSL usage examples not allowed)?

Please, no lectures on Javascript security and network security. This is not meant to protect against internet/network sniffing.


Solution

  • Found a solution in a comment on php.net for decrypting using OpenSSL, removed the OpenSSL part and replaced it with phpseclib (which in turn uses php's mcrypt, or its own software only implementation).

    require_once("Crypt/AES.php");
    
    //https://www.pidder.com/pidcrypt/?page=demo_aes-cbc
    //"U2FsdGVkX18KqDRV4bSqJCauxP7004rQmPu3HD1FUrgbXEGpjJAKAfgYvxtWJ6iJ"
    //Used the password: "password".
    var_export(pidCryptAESCBCDecrypt("U2FsdGVkX18KqDRV4bSqJCauxP7004rQmPu3HD1FUrgbXEGpjJAKAfgYvxtWJ6iJ", "password"));
    //Outputs: "Some text to encrypt..."
    
    function pidCryptAESCBCDecrypt($strBase64OpenSSLCompatibleCipher, $strPassword)
    {
        $data=base64_decode($strBase64OpenSSLCompatibleCipher);
        $salt=substr($data, 8, 8);
        $ct=substr($data, 16);
    
        /**
        * From https://github.com/mdp/gibberish-aes
        *
        * Number of rounds depends on the size of the AES in use
        * 3 rounds for 256
        *        2 rounds for the key, 1 for the IV
        * 2 rounds for 128
        *        1 round for the key, 1 round for the IV
        * 3 rounds for 192 since it's not evenly divided by 128 bits
        */
    
        $rounds = 3;
        $data00 = $strPassword.$salt;
        $md5_hash = array();
        $md5_hash[0] = md5($data00, true);
        $result = $md5_hash[0];
        for ($i = 1; $i < $rounds; $i++) {
        $md5_hash[$i] = md5($md5_hash[$i - 1].$data00, true);
        $result .= $md5_hash[$i];
        }
        $key = substr($result, 0, 32);
        $iv  = substr($result, 32,16);
    
    
        $aes=new Crypt_AES(CRYPT_AES_MODE_CBC);
        $aes->setKey($key);
        $aes->setIV($iv);
        echo $aes->decrypt($ct);
    }
    

    Update: Even simpler (can't believe I didn't see it), send to phpseclib pidCryptUtil.encodeBase64(aes.pidcrypt.getParams().encryptOut), aes.pidcrypt.getParams().iv and aes.pidcrypt.getParams().key.