5

I need to verify in-app purchases made in Windows 8 applications server-side using PHP. MSDN's documentation page has an example only in C#. Right now I've spent a whole day by searching for a way to do it in PHP. No success. All over the internet there are only .NET examples on this topic.

I've found some partial information about signed XML and x509, some libraries (xmlseclibs - useless, uses openssl_* functions which do not support sha256; phpseclib - looks promising but their documentation and examples are poor with no help).

Is it possible to do it somehow without learning everything about signed XML, RSA and x509? Right now I've read almost everything but everywhere is info only about encoding. Nothing about verifying.

neubert
  • 15,947
  • 24
  • 120
  • 212
Speedy
  • 1,361
  • 1
  • 14
  • 16

2 Answers2

1

I managed to verify WP8 IAP receipt using xmlseclibs library.

Also, you need php curl enabled.

do {
    $doc = new DOMDocument();

    $xml = $_POST['receipt_data']; // your receipt xml here!

    // strip unwanted chars - IMPORTANT!!!
    $xml = str_replace(array("\n","\t", "\r"), "", $xml);
    //some (probably mostly WP8) receipts have unnecessary spaces instead of tabs
    $xml = preg_replace('/\s+/', " ", $xml);
    $xml = str_replace("> <", "><", $xml);

    $doc->loadXML($xml);
    $receipt = $doc->getElementsByTagName('Receipt')->item(0);
    $certificateId = $receipt->getAttribute('CertificateId');

    $ch = curl_init("https://lic.apps.microsoft.com/licensing/certificateserver/?cid=$certificateId");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

    $publicKey = curl_exec($ch);
    $errno = curl_errno($ch);
    $errmsg = curl_error($ch);
    curl_close($ch);

    if ($errno != 0) {
        $verifyFailed = true;
        break;
    }

    // Verify xml signature
    require('./xmlseclibs.php');
    $objXMLSecDSig = new XMLSecurityDSig();
    $objDSig = $objXMLSecDSig->locateSignature($doc);
    if (!$objDSig) {
        $verifyFailed = true;
        break;
    }
    try {
        $objXMLSecDSig->canonicalizeSignedInfo();
        $retVal = $objXMLSecDSig->validateReference();
        if (!$retVal) {
            throw new Exception("Error Processing Request", 1);
        }
        $objKey = $objXMLSecDSig->locateKey();
        if (!$objKey) {
            throw new Exception("Error Processing Request", 1);
        }
        $key = NULL;
        $objKeyInfo = XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig);
        if (! $objKeyInfo->key && empty($key)) {
            $objKey->loadKey($publicKey);
        }
        if (!$objXMLSecDSig->verify($objKey)) {
            throw new Exception("Error Processing Request", 1);
        }
    } catch (Exception $e) {
        $verifyFailed = true;
        break;
    }

    $productReceipt = $doc->getElementsByTagName('ProductReceipt')->item(0);
    $prodictId = $productReceipt->getAttribute('ProductId');
    $purchaseDate = $productReceipt->getAttribute('PurchaseDate');
} while(0);

if ($verifyFailed) {
    // invalid receipt
} else {
    // valid receipt
}
Stackia
  • 2,110
  • 17
  • 23
-1

Speak for yourself. I love phpseclib's "build your own example" approach to documentation.

That said, I don't think phpseclib can really be used in this case. So you have the SignatureValue tag. What does that signature cover though? With XML Signatures, my understanding is that the signature covers a normalized form of the XML. xmlseclibsnormalizes an XML doc but phpseclib doesn't as it's a crypto lib - not an XML lib.

neubert
  • 15,947
  • 24
  • 120
  • 212
  • Do not get me wrong, I didn't mean to be rude, phpseclib looks like a strong library. But I missed more detailed examples like what to load in $x509->loadCA('...'); etc. I wanted to use it to verify xml signature by provided certificate as it is written in MS documentation. And verification should be possible (somehow) by this lib. I found this example [link](http://phpseclib.sourceforge.net/x509/examples.html#validate,loadx509,signoself,selfsetdnprop,csrload,crlload,issuersetdnprop,subjsetdnprop,) But I'm not sure what to use for validation. – Speedy Dec 04 '12 at 14:18
  • I think it's unreasonable to expect phpseclib to discuss how it can be validated against other X.509 implementations. phpseclib's website should document phpseclib - not other software packages. That said it does provide an interoperability page that should serve your purposes: http://phpseclib.sourceforge.net/interop.html –  Apr 07 '13 at 23:31