Как проверить хэш UID на стороне сервера Apple AppReceipt в PHP?

Я задаю этот вопрос, чтобы поделиться кодом решения.

Контекст: Apple представила AppReceipt в iOS 7. Он также присутствует в IAP OS X. Эта квитанция представляет собой контейнер PKCS#7 (asn.1) с полезной нагрузкой, которая также имеет структуру asn.1. В документации Apple указано, как контролировать действительность квитанции на устройстве и анализировать ее, чтобы убедиться, что она была выдана для текущего устройства. Также есть инструкции по проверке квитанции через сервер приложений, связавшись с сервером Apple. Однако в последнем случае возвращенные данные JSON от Apple не включают информацию, идентифицирующую исходное устройство. Предыдущая модель протокола IAP с transactionReceipt включала идентификатор идентификатораForVendor UID в файле json.

Вопрос. Как с помощью PHP проанализировать бинарный чек на сервере, чтобы проверить хэш UID и убедиться, что этот чек принадлежит этому устройству? Это можно сделать до или после отправки квитанции на сервер Apple.


person Max_B    schedule 18.06.2014    source источник


Ответы (1)


Этот скрипт проверяет только хеш, а не всю действительность подписи квитанции. Эта работа остается за Apple, отправляя им документально подтвержденную квитанцию.

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

Этот код использует анализатор ASN1 от Криса Бейли, ссылка также находится в исходном коде.

Вам нужно изменить один комментарий в коде скрипта парсера: строку комментария #189 и раскомментировать #190. Также последняя функция в скрипте парсера не используется и может быть удалена.

<?php

//$vendID should be a binary string. If you have the vendorID as an ASCII string, convert it back
// $vendID = hex2bin(str_replace('-', '', $vendID_string)); //PHP 5.4+
$vendID = hextobin(str_replace('-', '', $vendID_string));     //PHP 5.3- function below

require_once 'ans1.php'; //donwnload from http://www.phpkode.com/source/s/mistpark-server/library/asn1.php
$asn_parser = new ASN_BASE;
//parse the receipt binary string
$pkcs7 = $asn_parser->parseASNString($receipt->bin);
// $asn_parser->printASN($pkcs7); //uncomment this line to print and inspect PKCS7 container

//target the payload object inside the container
$payload_sequence = $pkcs7[0]->asnData[1]->asnData[0]->asnData[2]->asnData;
//control the OID of payload
if ($payload_sequence[0]->asnData != '1.2.840.113549.1.7.1') {
     echo "invalide payload OID";
     exit;
}
//the payload octet_string is itself an ASN1 structure. Parse it.
$payload = $asn_parser->parseASNString($payload_sequence[1]->asnData[0]->asnData);
// $asn_parser->printASN($payload); //uncomment this line to print and inspect payload ASN structure
$payload_attributes = $payload[0]->asnData; //array of ASN_SEQUENCE

foreach ($payload_attributes as $attr) {
     $type = $attr->asnData[0]->asnData;
     switch ($type) {
        case 2:
            $bundle_id = $attr->asnData[2]->asnData;
            break;
        // case 3:
        //     $bundle_version = $attr->asnData[2]->asnData;
        //     break;
        case 4:
            $opaque = $attr->asnData[2]->asnData;
            break;
        case 5:
            $hash = $attr->asnData[2]->asnData;
               break;          
     default:
          break;
     }
}
//compute the hash
$hash_loc = sha1($vendID . $opaque . $bundle_id, true);
//control hash equality
if ($hash_loc == $hash) {
     echo "OK\n";
}
else {
     echo "KO\n";
}

echo "</pre>\n";


//*******************************************************

function hextobin($hexstr) { 
    $n = strlen($hexstr); 
    $sbin = '';   
     for ($i = 0; $i < $n; $i += 2) $sbin .= pack("H*", substr($hexstr,$i,2));
    return $sbin; 
}


?>
person Max_B    schedule 18.06.2014