Хобрук: Ваш путь к мастерству в программировании

Обновление формы Sagepay с 2.22 до 3.00 — обновление PHP до шифрования AES

Я безуспешно пытался понять, как обновить шифрование, используемое в коде для трех сайтов электронной коммерции моей компании, с simpleXor на шифрование AES. Без этого я не могу обновить остальную часть кода, что означает, что после июля мы не сможем принимать платежи онлайн.

Мне удалось обновить коды криптоформ в соответствии с обновлением до 3.00.

Я могу определить код шифрования и просмотрел загрузку демо-версии формы интеграции для PHP от Sagepay, но не могу найти ничего, что хоть отдаленно похоже на мой код шифрования?!

Может ли кто-нибудь указать мне правильное направление для поиска подходящего кода шифрования для замены старого!?

Наши сайты основаны на JShop и у меня есть один файл для отправки и файл ответов.

Это файл для отправки информации:

    <?php   function startProcessor($orderNumber) {     global $dbA,$orderArray,$jssStoreWebDirHTTP,$jssStoreWebDirHTTPS,$cartMain;             $callBack = "$jssStoreWebDirHTTPS"."gateways/response/protx.php";               $cDetails = returnCurrencyDetails($orderArray["currencyID"]);       $gatewayOptions = retrieveGatewayOptions("PROTX");      switch ($gatewayOptions["testMode"]) {          case "S":               $myAction = "https://test.sagepay.com/Simulator/VSPFormGateway.asp";                break;          case "Y":               $myAction = "https://test.sagepay.com/gateway/service/vspform-register.vsp";                break;          case "N":               $myAction = "https://live.sagepay.com/gateway/service/vspform-register.vsp";                break;      }       $myVendor = $gatewayOptions["vendor"];      $myEncryptionPassword = $gatewayOptions["encryptionPassword"];              $billingAddress  = $orderArray["address1"]."\n";        if ($orderArray["address2"] != "") {            $billingAddress .= $orderArray["address2"]."\n";        }       $billingAddress .= $orderArray["town"]."\n";        $billingAddress .= $orderArray["county"]."\n";      $billingAddress .= $orderArray["country"];      $deliveryAddress  = $orderArray["deliveryAddress1"]."\n";       if ($orderArray["deliveryAddress2"] != "") {            $deliveryAddress .= $orderArray["deliveryAddress2"]."\n";       }       $deliveryAddress .= $orderArray["deliveryTown"]."\n";       $deliveryAddress .= $orderArray["deliveryCounty"]."\n";     $deliveryAddress .= $orderArray["deliveryCountry"];                     $crypt = "VendorTxCode=$orderNumber";
        $crypt .= "&Amount=".number_format($orderArray["orderTotal"],$cDetails["decimals"],'.','');
        $crypt .= "&Currency=".@$cDetails["code"];
        $crypt .= "&Description=".$gatewayOptions["description"];
        $crypt .= "&SuccessURL=$callBack?xOid=$orderNumber&xRn=".$orderArray["randID"];
        $crypt .= "&FailureURL=$callBack?xOid=$orderNumber&xRn=".$orderArray["randID"];
        $crypt .= "&BillingSurname=".$orderArray["surname"];
        $crypt .= "&BillingFirstnames=".$orderArray["forename"];
        $crypt .= "&BillingAddress1=".$orderArray["address1"];
        $crypt .= "&BillingCity=".$orderArray["town"];
        $crypt .= "&BillingPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["postcode"]);
        $crypt .= "&BillingCountry=".$orderArray["country"];
        $crypt .= "&DeliverySurname=".&orderArray["surname"];
        $crypt .= "&DeliveryFirstnames=".&orderArray["forename"];

    if ($orderArray["deliveryPostcode"] != "") {
        $crypt .= "&DeliveryAddress1=".$orderArray["deliveryAddress1"];
        $crypt .= "&DeliveryCity=".$orderArray["deliveryTown"];
        $crypt .= "&DeliveryPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["deliveryPostcode"]);
        $crypt .= "&DeliveryCountry=".$orderArray["deliveryCountry"]; }
    else {
        $crypt .= "&DeliveryAddress1=".$orderArray["address1"];
        $crypt .= "&DeliveryCity=".$orderArray["town"];
        $crypt .= "&DeliveryPostCode=".preg_replace("/[^\s\-a-zA-Z0-9]/", "", $orderArray["postcode"]);
        $crypt .= "&DeliveryCountry=".$orderArray["country"]; }

        $crypt .= "&BillingPhone=".preg_replace("/[^\sa-zA-Z0-9]/", "", $orderArray["telephone"]);      
if ($gatewayOptions["sendEmail"] == 1) {            
        $crypt .= "&CustomerEmail=".$orderArray["email"];       }
        $crypt .= "&VendorEmail=".$gatewayOptions["vendorEmail"];   
        $crypt .= "&ApplyAVSCV2=".$gatewayOptions["cvvCheck"];  
        $crypt .= "&Apply3DSecure=".$gatewayOptions["3DSecure"];            
        $crypt = base64_encode(protx_simpleXor($crypt,$myEncryptionPassword));      
        $tpl = createTSysObject(templatesCreatePath($cartMain["templateSet"]),"gatewaytransfer.html",$requiredVars,0);          
        $gArray["method"] = "POST"; 
        $gArray["action"] = $myAction;  
        $gArray["fields"][] = array("name"=>"VPSProtocol","value"=>"3.00"); 
        $gArray["fields"][] = array("name"=>"Vendor","value"=>$myVendor);   
        $gArray["fields"][] = array("name"=>"TxType","value"=>$gatewayOptions["txType"]);               $gArray["fields"][] = array("name"=>"Crypt","value"=>$crypt);           
        $mArray = $gArray;          
        $gArray["process"] = "document.automaticForm.submit();";    
        $tpl->addVariable("shop",templateVarsShopRetrieve());   
        $tpl->addVariable("labels",templateVarsLabelsRetrieve());   
        $tpl->addVariable("automaticForm",$gArray); 
        $tpl->addVariable("manualForm",$mArray);    
        $tpl->showPage();   }

function protx_simpleXor($inString, $key) {     $outString="";      $l=0;       if (strlen($inString)!=0) {         for ($i = 0; $i < strlen($inString); $i++) {                $outString=$outString . ($inString[$i]^$key[$l]);               $l++;               if ($l==strlen($key)) { $l=0; }         }       }       return $outString;  }   ?>

Это файл ответов:

 <?php

/*================ JShop Server ================

  = (c)2003-2010 Whorl Ltd.                    =

  = All Rights Reserved                        =

  = Redistribution of this file is prohibited. =

  = http://www.jshop.co.uk/                    =

  ==============================================*/

?><?php

    define("IN_JSHOP", TRUE);

    include("../../static/config.php");

    include("../../routines/dbAccess_".$databaseType.".php");

    include("../../routines/tSys.php");

    include("../../routines/general.php");

    include("../../routines/stockControl.php");

    include("../../routines/emailOutput.php");



    dbConnect($dbA);



    $orderID = makeSafe(getFORM("xOid"));

    $newOrderID = $orderID;

    $randID = makeSafe(getFORM("xRn"));

    $crypt = makeSafe(getFORM("crypt"));



    $gatewayOptions = retrieveGatewayOptions("PROTX");



    $orderID = makeInteger($orderID) - retrieveOption("orderNumberOffset");



    $result =  $dbA->query("select * from $tableOrdersHeaders where orderID=$orderID and randID='$randID'");

    if ($dbA->count($result) == 0 || $crypt=="") {

        doRedirect_JavaScript($jssStoreWebDirHTTP."index.php");

        exit;

    }

    $orderArray = $dbA->fetch($result);

    $ccResult = $dbA->query("select * from $tablePaymentOptions where paymentID=".$orderArray["paymentID"]);

    $poRecord = $dbA->fetch($ccResult);

    $paidStatus = $poRecord["statusID"];



    $crypt = str_replace(" ","+",$crypt);

    $crypt = protx_simpleXor(base64_decode($crypt),$gatewayOptions["encryptionPassword"]);  



    $nameValues = explode("&",$crypt);

    $resultCode = "";

    for ($f = 0; $f < count($nameValues); $f++) {

        $thisCode = explode("=",$nameValues[$f]);

        $resultCode[$thisCode[0]] = $thisCode[1];

    }



    if ($resultCode["VendorTxCode"] != $newOrderID) {

        doRedirect_JavaScript($jssStoreWebDirHTTP."index.php");

        exit;

    }



    $authResponse = "&Status Result=".$resultCode["Status"]."&AVS/CV2 Check=".@$resultCode["AVSCV2"]."&Address Result=".@$resultCode["AddressResult"]."&Postcode Result=".@$resultCode["PostCodeResult"]."&CV2 Result=".@$resultCode["CV2Result"]."&3d Secure Status=".@$resultCode["3DSecureStatus"];



    $randID = $orderArray["randID"];

    if ($orderArray["status"] != $paidStatus) {

            $dt=date("YmdHis",createOffsetTime());

            switch ($resultCode["Status"]) {

                case "OK":

                case "AUTHENTICATED":

                case "REGISTERED":

                    $authResponse="Gateway=Sage Pay&Authorisation Code=".$resultCode["TxAuthNo"]."&Sage Pay Transaction ID=".$resultCode["VPSTxId"]."&Status=Payment Confirmed".$authResponse;

                    $dbA->query("update $tableOrdersHeaders set status=$paidStatus, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");

                    $orderArray["status"] = $paidStatus;

                    //ok, this is where we should do the stock control then.

                    include("process/paidProcessList.php");

                    doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");

                    break;

                case "REJECTED":
                    $authResponse="Gateway=Sage Pay&Status=Payment Rejected Due To Rules".$authResponse;
                    $dbA->query("update $tableOrdersHeaders set status=3, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");
                    include("process/failProcessList.php");
                    doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
                    break;

                default:
                    if ($orderArray["status"] == 1) {
                        $authResponse="Gateway=Sage Pay&Status=Payment Failed".$authResponse;
                        $dbA->query("update $tableOrdersHeaders set status=3, authInfo=\"$authResponse\", paymentDate=\"$dt\" where orderID=$orderID");
                        include("process/failProcessList.php");
                }

                    doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
                    break;
            }
    } else {
            doRedirect_JavaScript($jssStoreWebDirHTTPS."process.php?xOid=$newOrderID&xRn=$randID");
    }

    function protx_simpleXor($inString, $key) {
        $outString="";

        $l=0;

        if (strlen($inString)!=0) {
            for ($i = 0; $i < strlen($inString); $i++) {
                $outString=$outString . ($inString[$i]^$key[$l]);
                $l++;

                if ($l==strlen($key)) { $l=0; }
            }
        }

        return $outString;
    }
?>
15.06.2015

  • Там много всего, если вы ищете «код шифрования sagepay php». Большинство из них используют AES с mcrypt, поэтому попробуйте поискать «MCRYPT_RIJNDAEL_128» в образцах кода, которые вы найдете. 15.06.2015
  • Кстати, из любопытства, есть ли в вашем исходном коде все эти пустые строки, или это была случайность копирования/вставки? 16.06.2015
  • Извините, я новичок на форуме и, очевидно, мне нужно поработать над своей грамматикой :-) кажется, что при копировании и вставке добавлены строки, поскольку в файле php это не так. У меня был поиск в Интернете, но я не смог найти ничего похожего на то, что находится в коде на данный момент - я не в курсе кода с php и шифрованием, но я посмотрю, что еще я могу найти. 16.06.2015

Ответы:


1

Похоже, вы используете очень старый код. Я бы порекомендовал вам попробовать полностью портировать/переписать свой код, используя официальные библиотеки интеграции Sage Pay, чтобы избежать странных ошибок с такими вещами, как экранирование.

Если вы спешите, вот урезанная версия класса, который вам нужен, чтобы заставить работать часть шифрования/дешифрования.

<?php
class SagepayUtil
{
    /**
     * PHP's mcrypt does not have built in PKCS5 Padding, so we use this.
     *
     * @param string $input The input string.
     *
     * @return string The string with padding.
     */
    static protected function addPKCS5Padding($input)
    {
        $blockSize = 16;
        $padd = "";

        // Pad input to an even block size boundary.
        $length = $blockSize - (strlen($input) % $blockSize);
        for ($i = 1; $i <= $length; $i++)
        {
            $padd .= chr($length);
        }

        return $input . $padd;
    }

    /**
     * Remove PKCS5 Padding from a string.
     *
     * @param string $input The decrypted string.
     *
     * @return string String without the padding.
     * @throws SagepayApiException
     */
    static protected function removePKCS5Padding($input)
    {
        $blockSize = 16;
        $padChar = ord($input[strlen($input) - 1]);

        /* Check for PadChar is less then Block size */
        if ($padChar > $blockSize)
        {
            throw new SagepayApiException('Invalid encryption string');
        }
        /* Check by padding by character mask */
        if (strspn($input, chr($padChar), strlen($input) - $padChar) != $padChar)
        {
            throw new SagepayApiException('Invalid encryption string');
        }

        $unpadded = substr($input, 0, (-1) * $padChar);
        /* Chech result for printable characters */
        if (preg_match('/[[:^print:]]/', $unpadded))
        {
            throw new SagepayApiException('Invalid encryption string');
        }
        return $unpadded;
    }

    /**
     * Encrypt a string ready to send to SagePay using encryption key.
     *
     * @param  string  $string  The unencrypyted string.
     * @param  string  $key     The encryption key.
     *
     * @return string The encrypted string.
     */
    static public function encryptAes($string, $key)
    {
        // AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
        // Add PKCS5 padding to the text to be encypted.
        $string = self::addPKCS5Padding($string);

        // Perform encryption with PHP's MCRYPT module.
        $crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);

        // Perform hex encoding and return.
        return "@" . strtoupper(bin2hex($crypt));
    }

    /**
     * Decode a returned string from SagePay.
     *
     * @param string $strIn         The encrypted String.
     * @param string $password      The encyption password used to encrypt the string.
     *
     * @return string The unecrypted string.
     * @throws SagepayApiException
     */
    static public 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);

        // Throw exception if string is malformed
        if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
        {
            throw new SagepayApiException('Invalid encryption string');
        }
        $strIn = pack('H*', $hex);

        // Perform decryption with PHP's MCRYPT module.
        $string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector);
        return self::removePKCS5Padding($string);
    }
}

Настройте свои переменные, как вы это делаете сейчас.

$gatewayOptions["encryptionPassword"] = "PASSWORDPASSWORD";
$crypt = "Your=Sagepay&Query=String";

Затем для шифрования (отправки на sagepay) используйте этот

//$crypt = base64_encode(protx_simpleXor($crypt,$myEncryptionPassword));
$crypt = SagepayUtil::encryptAes($crypt, $gatewayOptions["encryptionPassword"]);

var_dump($crypt);
//@714DCF7FE82FADA4B9F8CF53982D56FFC8F2021A5FC10481409ED1FD7BF6880E

А для расшифровки (получения от sagepay) использовать вот это

//$crypt = protx_simpleXor(base64_decode($crypt),$gatewayOptions["encryptionPassword"]);  
$crypt = SagepayUtil::decryptAes($crypt, $gatewayOptions["encryptionPassword"]);

var_dump($crypt);
//Your=Sagepay&Query=String

Изменить: удален слой base64encoding. Protx предполагает, что строка в кодировке base64 использует старый метод XOR, поэтому вы не должны использовать кодировку base64. Вместо этого необходимо использовать шестнадцатеричное кодирование и начинать строку с символа «@».

17.06.2015
  • Я добавил это в текущий код (переписывание кода для меня не вариант), и я все еще получаю код ошибки неподдерживаемого метода шифрования (5068) от Sagepay. Любые идеи, почему это может быть? 29.06.2015
  • Как выглядит ваше значение $crypt? 29.06.2015
  • Ах да извините. Вам не нужен этот дополнительный слой base64. 29.06.2015
  • Спасибо, Фил, base64 был удален, и шифрование теперь работает! 30.06.2015
  • Новые материалы

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

    Разбор строк запроса в vue.js
    Иногда вам нужно получить данные из строк запроса, в этой статье показано, как это сделать. В жизни каждого дизайнера/разработчика наступает момент, когда им необходимо беспрепятственно..

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..