AES/CBC/PKCS#5 Encryption Algorithm In PHP

slickboy picture slickboy · Nov 26, 2013 · Viewed 11.8k times · Source

Im attempting to integrate the SagePay payment gateway into a website using the 'Form Integration' method.

Basically the form integration method works by inserting a FORM in a webpage and POSTing information to SagePay's servers whenever the Submit button for the FORM is selected. Before the information can be sent to SagePay's servers it must be encrypted using the AES/CBC/PKCS#5 algorithm, before being Base 64 encoded.

I have a basic knowledge of encryption but I have no experience of using it in PHP. Can anyone please help me formulate the AES/CBC/PKCS#5 algorithm in PHP please?

Here's my efforts so far:

$CRYPT = "Text Goes Here";

$blocksize = 16;//Does 16 Refer To 16 Bytes Or 16 Bits? 16 Bytes = 128 Bits.
$cryptLength = strlen($CRYPT);
$cryptLengthModuloBlocksize = $cryptLength % $blocksize;
$pad = $blocksize - $cryptLengthModuloBlocksize;
$padString = str_repeat(chr($pad), $pad);
$CRYPT = $CRYPT . $padString;

$encryptionPassword = 'password';
$Encrypted_CRYPT = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $encryptionPassword, $CRYPT, MCRYPT_MODE_CBC);

$Base64_Encrypted_CRYPT = base64_encode($Encrypted_CRYPT);

echo    "<form action = 'https://test.sagepay.com/gateway/service/vspform-register.vsp' method = 'POST'>

            <input type = 'hidden' name = 'VPSProtocol' value = '3.00' />//
            <input type = 'hidden' name = 'TxType' value = 'PAYMENT' />
            <input type = 'hidden' name = 'Vendor' value = 'vendorName' />
            <input type = 'hidden' name = 'Crypt' value = '" . $Base64_Encrypted_CRYPT . "' />
            <input type= 'submit' value = 'Submit'>

        </form>";

Any help is much appreciated.

Answer

evo_rob picture evo_rob · Jan 30, 2014

Here is a working implementation of AES/CBC/PKCS#5 working with Sagepay's form integration You will need mcrypt installed. sp_encryption is a define for the encryption key.

/**
* @param string $data - the key=value pairs separated with & 
* @return string
*/
protected function encode_data($data) {
    $strIn = $this->pkcs5_pad($data);
    $strCrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, sp_encryption, $strIn, MCRYPT_MODE_CBC, sp_encryption);
    return "@" . bin2hex($strCrypt);
}

/**
* @param string $data - crypt response from Sagepay
* @return string
*/
protected function decode_data($data) {
    if (substr($data, 0, 1) == "@") {
        $strIn = hex2bin(substr($data, 1));
        return $this->pkcs5_unpad(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, sp_encryption, $strIn, MCRYPT_MODE_CBC, sp_encryption));
    }
    return '';
}

protected function pkcs5_pad($text) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $pad = $size - (strlen($text) % $size);
    return $text . str_repeat(chr($pad), $pad);
}

protected function pkcs5_unpad($text) {
    $pad = ord($text{strlen($text) - 1});
    if ($pad > strlen($text)) return false;
    if (strspn($text, $text{strlen($text) - 1}, strlen($text) - $pad) != $pad) {
        return false;
    }
    return substr($text, 0, -1 * $pad);
}

On a side note make sure the VPSProtocol you use is '3.00' not 3, and not 3.0 as these won't work!

Hoep this helps and sagepay get some official PHP docs out soon.