Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/120.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
后端iOS iap收据验证,不带/验证收据请求_Ios_Validation_In App Purchase_Backend_Receipt - Fatal编程技术网

后端iOS iap收据验证,不带/验证收据请求

后端iOS iap收据验证,不带/验证收据请求,ios,validation,in-app-purchase,backend,receipt,Ios,Validation,In App Purchase,Backend,Receipt,我想在我的后端代码中验证iOS应用程序内购买收据 苹果公司使用外部/verifyReceipt请求来实现这一点的设计决策非常愚蠢:它会导致延迟并增加网络错误处理的复杂性。更重要的是,收据中的数据看起来可以通过公钥验证 在对收据的签名字段进行一点分析后,它似乎包含一个经过PK验证的SHA1哈希: <?php $sig="ApxQMks+KAE0riYtKjNNwhNeuGQ6R98X223zCh60s9m8wloydP3sCceQdzrCwd/3N1L+dlefT7ZJUiquCEsDAo+

我想在我的后端代码中验证iOS应用程序内购买收据

苹果公司使用外部/verifyReceipt请求来实现这一点的设计决策非常愚蠢:它会导致延迟并增加网络错误处理的复杂性。更重要的是,收据中的数据看起来可以通过公钥验证

在对收据的
签名
字段进行一点分析后,它似乎包含一个经过PK验证的SHA1哈希:

<?php
$sig="ApxQMks+KAE0riYtKjNNwhNeuGQ6R98X223zCh60s9m8wloydP3sCceQdzrCwd/3N1L+dlefT7ZJUiquCEsDAo+Rh54eSovcKEk+2RZyoP/zRQHgTF81kYBIbkFCADhj6kzJVr1rYsRXKpOJk6qWMYPz+a90XJfGtnIDuHlRb4V5AAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";

file_put_contents('sig', substr(base64_decode($sig),1,128));
file_put_contents('cert.der', substr(base64_decode($sig),133));

# show certificate
echo `openssl x509 -in cert.der -inform der -noout -text` . "\n\n";

# convert to pem
`openssl x509 -in cert.der -inform der -out cert.pem`;

echo "signature:\n";
echo `openssl rsautl -in sig -verify -asn1parse -inkey cert.pem -certin`;
echo "\n\n";
现在的问题仍然是这是哪个数据的散列。上述内容包含本收据的实际签名:

{
        "signature" = "ApxQMks+KAE0riYtKjNNwhNeuGQ6R98X223zCh60s9m8wloydP3sCceQdzrCwd/3N1L+dlefT7ZJUiquCEsDAo+Rh54eSovcKEk+2RZyoP/zRQHgTF81kYBIbkFCADhj6kzJVr1rYsRXKpOJk6qWMYPz+a90XJfGtnIDuHlRb4V5AAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";
        "purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE0LTAxLTI3IDA0OjUwOjU2IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInB1cmNoYXNlLWRhdGUtbXMiID0gIjEzOTA4MjcwNTYxNzciOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJiMTUxOTczZThjYzY3ZGRlMzkwNzhiZDAwMGU4N2U3MjNiYjE0M2U1IjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDA5OTcwODE1MCI7CgkiYnZycyIgPSAiMS4wLjciOwoJImFwcC1pdGVtLWlkIiA9ICI3MTE0MTEzMTciOwoJInRyYW5zYWN0aW9uLWlkIiA9ICIxMDAwMDAwMDk5NzA4MTUwIjsKCSJxdWFudGl0eSIgPSAiMSI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZS1tcyIgPSAiMTM5MDgyNzA1NjE3NyI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICJCNjNBRTVFQy02MjBCLTQxMkEtQjE5NC03NUI3MDU3Mjk4M0MiOwoJIml0ZW0taWQiID0gIjc3NTg5MTg5MiI7CgkidmVyc2lvbi1leHRlcm5hbC1pZGVudGlmaWVyIiA9ICIxNzU5NTMxMzgiOwoJInByb2R1Y3QtaWQiID0gImNvbS5pbnRvbXlsaWZlLmNyZWRpdHMyNSI7CgkicHVyY2hhc2UtZGF0ZSIgPSAiMjAxNC0wMS0yNyAxMjo1MDo1NiBFdGMvR01UIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDE0LTAxLTI3IDEyOjUwOjU2IEV0Yy9HTVQiOwoJImJpZCIgPSAiY29tLmludG9teWxpZmUuaW9zIjsKCSJwdXJjaGFzZS1kYXRlLXBzdCIgPSAiMjAxNC0wMS0yNyAwNDo1MDo1NiBBbWVyaWNhL0xvc19BbmdlbGVzIjsKfQ==";
        "environment" = "Sandbox";
        "pod" = "100";
        "signing-status" = "0";
}
或者,在base64中:

$receipt="ewoJInNpZ25hdHVyZSIgPSAiQXB4UU1rcytLQUUwcmlZdEtqTk53aE5ldUdRNlI5OFgyMjN6Q2g2MHM5bTh3bG95ZFAzc0NjZVFkenJDd2QvM04xTCtkbGVmVDdaSlVpcXVDRXNEQW8rUmg1NGVTb3ZjS0VrKzJSWnlvUC96UlFIZ1RGODFrWUJJYmtGQ0FEaGo2a3pKVnIxcllzUlhLcE9KazZxV01ZUHorYTkwWEpmR3RuSUR1SGxSYjRWNUFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUwTFRBeExUSTNJREEwT2pVd09qVTJJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakV6T1RBNE1qY3dOVFl4TnpjaU93b0pJblZ1YVhGMVpTMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKaU1UVXhPVGN6WlRoall6WTNaR1JsTXprd056aGlaREF3TUdVNE4yVTNNak5pWWpFME0yVTFJanNLQ1NKdmNtbG5hVzVoYkMxMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURBNU9UY3dPREUxTUNJN0Nna2lZblp5Y3lJZ1BTQWlNUzR3TGpjaU93b0pJbUZ3Y0MxcGRHVnRMV2xrSWlBOUlDSTNNVEUwTVRFek1UY2lPd29KSW5SeVlXNXpZV04wYVc5dUxXbGtJaUE5SUNJeE1EQXdNREF3TURrNU56QTRNVFV3SWpzS0NTSnhkV0Z1ZEdsMGVTSWdQU0FpTVNJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlMxdGN5SWdQU0FpTVRNNU1EZ3lOekExTmpFM055STdDZ2tpZFc1cGNYVmxMWFpsYm1SdmNpMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKQ05qTkJSVFZGUXkwMk1qQkNMVFF4TWtFdFFqRTVOQzAzTlVJM01EVTNNams0TTBNaU93b0pJbWwwWlcwdGFXUWlJRDBnSWpjM05UZzVNVGc1TWlJN0Nna2lkbVZ5YzJsdmJpMWxlSFJsY201aGJDMXBaR1Z1ZEdsbWFXVnlJaUE5SUNJeE56VTVOVE14TXpnaU93b0pJbkJ5YjJSMVkzUXRhV1FpSUQwZ0ltTnZiUzVwYm5SdmJYbHNhV1psTG1OeVpXUnBkSE15TlNJN0Nna2ljSFZ5WTJoaGMyVXRaR0YwWlNJZ1BTQWlNakF4TkMwd01TMHlOeUF4TWpvMU1EbzFOaUJGZEdNdlIwMVVJanNLQ1NKdmNtbG5hVzVoYkMxd2RYSmphR0Z6WlMxa1lYUmxJaUE5SUNJeU1ERTBMVEF4TFRJM0lERXlPalV3T2pVMklFVjBZeTlIVFZRaU93b0pJbUpwWkNJZ1BTQWlZMjl0TG1sdWRHOXRlV3hwWm1VdWFXOXpJanNLQ1NKd2RYSmphR0Z6WlMxa1lYUmxMWEJ6ZENJZ1BTQWlNakF4TkMwd01TMHlOeUF3TkRvMU1EbzFOaUJCYldWeWFXTmhMMHh2YzE5QmJtZGxiR1Z6SWpzS2ZRPT0iOwoJImVudmlyb25tZW50IiA9ICJTYW5kYm94IjsKCSJwb2QiID0gIjEwMCI7Cgkic2lnbmluZy1zdGF0dXMiID0gIjAiOwp9";
要验证的最简单数据是
采购信息
字段。不幸的是,此数据(base64编码或不透明)的sha1和与签名中的不匹配

在看到了相当多的苹果文件格式之后,我猜测它可能是一种类似于
“{$purchaseData}\x00\x00{$environment}\x00\x00{$pod}”的组合形式
。不过,由于运气不好,他们添加了一个秘密字符串,这会使整个练习变得毫无意义(但我不明白他们为什么会…)

有什么见解吗

更新


向/verifyReceipt端点发送不同收据的更多实验表明,
pod
/
环境
字段并不重要。更重要的是,收据结构中字段的顺序无关紧要。但是,更改
采购信息
数据中的单个字节会直接生成无效收据。这一切都将强化一个假设,即只有
购买信息
值是散列的一部分,但它可能以一个秘密作为前缀/后缀。有人能验证(双关语)这一点吗?

请查看中的代码

签名结构是

#pragma pack(push, 1)
struct signature_blob {
    uint8_t version;
    uint8_t signature[128];
    uint32_t cert_len;
    uint8_t certificate[];
} *signature_blob_ptr = (struct signature_blob *)signature_bytes;
#pragma pack(pop)

to_be_hashed= signature_blob_ptr->version . base64_decode(purchase_info)
代码在这里

<?php
$plist = file_get_contents($argv[1]);
if(preg_match('/"signature" = "([^"]+)"/',$plist,$s) && preg_match('/"purchase-info" = "([^"]+)"/',$plist,$p)){
    $sig= base64_decode($s[1]);
    $purchase=base64_decode($p[1]);

    $version=substr($sig,0,1);
    $cert = substr($sig,133);
    $sig = substr($sig,1,128);
    $to_be_sign=$version.$purchase;
    //get PEM encode
    $cert="-----BEGIN CERTIFICATE-----\n".wordwrap(base64_encode($cert),64,"\n",true)."\n-----END CERTIFICATE-----";
    $pubkey = openssl_get_publickey($cert);
    if(openssl_verify($to_be_sign,$sig,$pubkey) ==1 )
            echo "Success\n";
    else
            echo "Failed\n";
    openssl_free_key($pubkey);
}

从iOS 7开始,您可以在本地完成所有操作。据我所知,本地验证使用的是不同的(PKCS#7)收据容器—信息不可用。我已经更新了这个问题来澄清。@OP你的链接是针对OSX的,但你谈论的是IOS-是哪个?我敢打赌,你在哪里看到OSX的东西?@EmielMols我很困惑。你说你想在本地做这件事,而不是在服务器上,但是你更新的问题是在服务器上做这件事。就像iOS 7一样,您不需要在服务器上执行任何操作。您可以在本地验证收据。我给你的链接帮助你做到这一点。很好!这真是太棒了!对于其他使用这种实现的人,我建议使用公钥的硬编码变体(似乎不太可能经常更改)。这个答案中的实现似乎没有检查允许邪恶eve在应用程序购买中对其进行自签名的证书链。感谢您提供php代码示例。它在我的服务器上运行得很好。我现在正在我们的服务器上进行整个收据验证,并从那里调用itunes验证。这非常有效,简化了客户端上的代码。
<?php
$plist = file_get_contents($argv[1]);
if(preg_match('/"signature" = "([^"]+)"/',$plist,$s) && preg_match('/"purchase-info" = "([^"]+)"/',$plist,$p)){
    $sig= base64_decode($s[1]);
    $purchase=base64_decode($p[1]);

    $version=substr($sig,0,1);
    $cert = substr($sig,133);
    $sig = substr($sig,1,128);
    $to_be_sign=$version.$purchase;
    //get PEM encode
    $cert="-----BEGIN CERTIFICATE-----\n".wordwrap(base64_encode($cert),64,"\n",true)."\n-----END CERTIFICATE-----";
    $pubkey = openssl_get_publickey($cert);
    if(openssl_verify($to_be_sign,$sig,$pubkey) ==1 )
            echo "Success\n";
    else
            echo "Failed\n";
    openssl_free_key($pubkey);
}