Skip to content
Advertisement

Sage Pay / Opayo Form integration – replacing mcrypt with openssl

We use the Sage Pay / Opayo form integration to pass customer orders to Sage Pay for payment, then to process the report that comes back. Sage Pay’s example code relies on the PHP mcrypt functions which are no longer supported from PHP 7.2, so we need to update the scripts to OpenSSL.

Encryption was covered by a great post here: Upgrade mcrypt to OpenSSL encryption in SagePay form

  • this works fine for me:
    function encryptAes_new ($string, $key) {
      $key = str_pad($key,16,""); # if supplied key is, or may be, less than 16 bytes
      $crypt = openssl_encrypt($string, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key);
      // Perform hex encoding and return.
      return "@" . strtoupper(bin2hex($crypt));
    }

…but I can’t find a matching code to fix the decrypt function.

The current code is:

    function decryptAes($strIn, $password)
    {
        // HEX decoding then AES decryption, CBC blocking with PKCS5 padding.
        // Use initialization vector (IV) set from $str_encryption_password.
        $strInitVector = $password;
    
        // Remove the first char which is @ to flag this is AES encrypted and HEX decoding.
        $hex = substr($strIn, 1);
    
        $strIn = pack('H*', $hex);
    
        // Perform decryption with PHP's MCRYPT module.
        $string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector);
        return removePKCS5Padding($string);
    }

Could anyone help with an OpenSSL version, please?

Advertisement

Answer

When decrypting, the @ prefix must first be removed, then hex decoding must be done, and finally decryption can be performed. openssl_decrypt() implicitly removes the padding, so that no explicit removal is required. This is implemented in decryptAes_new() below:

<?php
function encryptAes_new ($string, $key) {
    $key = str_pad($key,16,""); # if supplied key is, or may be, less than 16 bytes
    $crypt = openssl_encrypt($string, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key);
    // Perform hex encoding and return.
    return "@" . strtoupper(bin2hex($crypt));
}

function decryptAes_new($string, $key) {
    $key = str_pad($key,16,""); 
    $binary = hex2bin(substr($string, 1));
    return openssl_decrypt($binary, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key);
}

$plaintext = 'The quick brown fox jumps over the lazy dog';
$key = '0123456789012345';
$ciphertext = encryptAes_new($plaintext, $key);
$decrypted = decryptAes_new($ciphertext, $key);
print('Ciphertext: ' . $ciphertext . PHP_EOL);
print('Plaintext:  ' . $decrypted . ' - Size: ' . strlen($decrypted) . PHP_EOL);
?>

which produces the output:

Ciphertext: @3089E6BC224BD95B85CF56F4B967118AAA4705430F25B6B4D953188AD15DD78F3867577E7D58E18C9CB340647C8B4FD8
Plaintext:  The quick brown fox jumps over the lazy dog - Size: 43

Checking the length of the plaintext after decryption, 43 bytes, shows that the padding has been automatically removed.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement