run home for php windows openssl

home - Validación de firma IAP de Windows Store contra certificado remoto, con PHP



windows 10 linux command line (2)

Estoy intentando validar un recibo de IAP en PHP para una aplicación de la Tienda Windows. Básicamente, tratando de convertir este código de ejemplo a PHP http://msdn.microsoft.com/en-us/library/windows/apps/jj649137.aspx . El recibo se ve así

<Receipt Version="1.0" ReceiptDate="2012-08-30T23:08:52Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528"> <ProductReceipt Id="6bbf4366-6fb2-8be8-7947-92fd5f683530" ProductId="Product1" PurchaseDate="2012-08-30T23:08:52Z" ExpirationDate="2012-09-02T23:08:49Z" ProductType="Durable" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" /> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> <DigestValue>Uvi8jkTYd3HtpMmAMpOm94fLeqmcQ2KCrV1XmSuY1xI=</DigestValue> </Reference> </SignedInfo> <SignatureValue>TT5fDET1X9nBk9/yKEJAjVASKjall3gw8u9N5Uizx4/Le9RtJtv+E9XSMjrOXK/TDicidIPLBjTbcZylYZdGPkMvAIc3/1mdLMZYJc+EXG9IsE9L74LmJ0OqGH5WjGK/UexAXxVBWDtBbDI2JLOaBevYsyy+4hLOcTXDSUA4tXwPa2Bi+BRoUTdYE2mFW7ytOJNEs3jTiHrCK6JRvTyU9lGkNDMNx9loIr+mRks+BSf70KxPtE9XCpCvXyWa/Q1JaIyZI7llCH45Dn4SKFn6L/JBw8G8xSTrZ3sBYBKOnUDbSCfc8ucQX97EyivSPURvTyImmjpsXDm2LBaEgAMADg==</SignatureValue> </Signature> </Receipt>

Recuperé un certificado para el servidor como este

function getCertificate($certID) { $url = ''https://lic.apps.microsoft.com/licensing/certificateserver/?cid='' . $certID; $path = ''/mypath/certs/'' . $certID; if(!file_exists($path)) { $fp = fopen($path, ''w''); $ch = curl_init($url); curl_setopt($ch, CURLOPT_FILE, $fp); $data = curl_exec($ch); curl_close($ch); fclose($fp); } $cert = file_get_contents($path); //var_dump(openssl_x509_parse($cert)); return openssl_x509_read($cert); }

y asumo que SignatureValue es mi firma. Por lo que puedo decir al leer todo, la función que necesito es openssl_verify, pero no estoy seguro de qué parámetros debería usar, ya que la verificación siempre falla.

$data = $receiptXML->Signature->SignatureValue; $pubkeyid = openssl_get_publickey($cert); // state whether signature is okay or not $ok = openssl_verify($receipt, $data, $pubkeyid, OPENSSL_ALGO_SHA256); if($ok == 1) { echo "good"; } elseif($ok == 0) { echo "bad"; } else { echo "ugly, error checking signature"; } // free the key from memory openssl_free_key($pubkeyid);

¿Alguien sabe dónde me he equivocado aquí?


Para empezar, recomendaría que el Certificado se escriba en modo binario. Lo hace menos propenso a los errores. Entonces, lo que recomendaría es

if(!file_exists($path)) { $fp = fopen($path, ''wb'');

Debo suponer que $ CERTID tendrá el valor de CertificateId del recibo XML. Cambie el nombre según lo haya declarado en su código.

$cert = getCertificate($CERTID) if($cert == 0) { echo "bad"; } else { $data = $receiptXML->Signature->SignatureValue; $pubkeyid = openssl_get_publickey($cert); // state whether signature is okay or not $ok = openssl_verify($receiptXML, $data, $pubkeyid, OPENSSL_ALGO_SHA256); if($ok == 1) { echo "good"; } elseif($ok == 0) { echo "bad"; } else { echo "ugly, error checking signature"; } }

Espero que esto ayude :)


Paso días para verificar los recibos y finalmente hacer que funcione ...

<?php /** * Date: 01.11.2013 * Time: 23:09 * @author: Philipp Serrer */ namespace Ephisa/Service/WindowsStore; require_once subpath . ''vendor/xmlseclibs/xmlseclibs.php''; use Ephisa/Cache; class Receipt { private $doc; private $objXMLSecDSig; private $objDSig; function __construct($xml, $isFile = false) { if ($isFile) { $xml = file_get_contents($xml); } // 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 = new /DOMDocument(); $doc->loadXML($xml); $objXMLSecDSig = new /XMLSecurityDSig(); $objDSig = $objXMLSecDSig->locateSignature($doc); if (!$objDSig) { throw new InvalidSignatureException(); } //canonicalize $objXMLSecDSig->canonicalizeSignedInfo(); $this->objDSig = $objDSig; $this->objXMLSecDSig = $objXMLSecDSig; $this->doc = $doc; } /** * Returns the key for verification. * * @return null|/XMLSecurityKey */ function getKey() { $objKey = $this->objXMLSecDSig->locateKey(); $keyInfo = /XMLSecEnc::staticLocateKeyInfo($objKey, $this->objDSig); if (!$keyInfo->key) { $xpath = new /DOMXPath($this->doc); $query = ''string(/Receipt/@CertificateId)''; $id = $xpath->evaluate($query); Cache::instance()->setLifetime(60*60*24*7, ''win-store-cert''); $cert = Cache::instance()->get($id, ''win-store-cert'', function() use ($id) { return file_get_contents(''https://lic.apps.microsoft.com/licensing/certificateserver/?cid='' . $id); }); $objKey->loadKey($cert, false); } return $objKey; } /** * Verifies the given receipt * * @return bool Returns TRUE on success */ function verify() { try { if (!$this->objXMLSecDSig->validateReference()) { return false; } return (bool)$this->objXMLSecDSig->verify($this->getKey()); } catch (/Exception $e) { // failure... } return false; } }

Este código forma parte de mi framework y, por lo tanto, contiene algún código dependiente y código (Cache), pero creo que obtienes la idea principal y cómo funciona. Por supuesto, debe incluir php xmlseclibs disponibles en https://github.com/robrichards/xmlseclibs