<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>https://sound.bai-hua.org/index.php?action=history&amp;feed=atom&amp;title=VerificationController.m</id>
		<title>VerificationController.m - Revision history</title>
		<link rel="self" type="application/atom+xml" href="https://sound.bai-hua.org/index.php?action=history&amp;feed=atom&amp;title=VerificationController.m"/>
		<link rel="alternate" type="text/html" href="https://sound.bai-hua.org/index.php?title=VerificationController.m&amp;action=history"/>
		<updated>2026-10-01T12:13:59Z</updated>
		<subtitle>Revision history for this page on the wiki</subtitle>
		<generator>MediaWiki 1.29.0</generator>

	<entry>
		<id>https://sound.bai-hua.org/index.php?title=VerificationController.m&amp;diff=359&amp;oldid=prev</id>
		<title>Admin: Created page with &quot;&lt;syntaxhighlight lang=&quot;objc&quot;&gt; // //  VerificationController.m //  iaptest // //  Created by zhangjw on 7/25/12. //  Copyright __MyCompanyName__ 2012. All rights reserved. //   #i...&quot;</title>
		<link rel="alternate" type="text/html" href="https://sound.bai-hua.org/index.php?title=VerificationController.m&amp;diff=359&amp;oldid=prev"/>
				<updated>2012-07-25T13:39:19Z</updated>
		
		<summary type="html">&lt;p&gt;Created page with &amp;quot;&amp;lt;syntaxhighlight lang=&amp;quot;objc&amp;quot;&amp;gt; // //  VerificationController.m //  iaptest // //  Created by zhangjw on 7/25/12. //  Copyright __MyCompanyName__ 2012. All rights reserved. //   #i...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;syntaxhighlight lang=&amp;quot;objc&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
//  VerificationController.m&lt;br /&gt;
//  iaptest&lt;br /&gt;
//&lt;br /&gt;
//  Created by zhangjw on 7/25/12.&lt;br /&gt;
//  Copyright __MyCompanyName__ 2012. All rights reserved.&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#import &amp;quot;VerificationController.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
static VerificationController *singleton;&lt;br /&gt;
&lt;br /&gt;
@implementation VerificationController&lt;br /&gt;
&lt;br /&gt;
+ (VerificationController *)sharedInstance&lt;br /&gt;
{&lt;br /&gt;
	if (singleton == nil)&lt;br /&gt;
    {&lt;br /&gt;
		singleton = [[VerificationController alloc] init];&lt;br /&gt;
	}&lt;br /&gt;
	return singleton;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (id)init&lt;br /&gt;
{&lt;br /&gt;
	self = [super init];&lt;br /&gt;
	if (self != nil)&lt;br /&gt;
    {&lt;br /&gt;
        transactionsReceiptStorageDictionary = [[NSMutableDictionary alloc] init];&lt;br /&gt;
	}&lt;br /&gt;
	return self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (NSDictionary *)dictionaryFromPlistData:(NSData *)data&lt;br /&gt;
{&lt;br /&gt;
    NSError *error;&lt;br /&gt;
    NSDictionary *dictionaryParsed = [NSPropertyListSerialization propertyListWithData:data&lt;br /&gt;
                                                                               options:NSPropertyListImmutable&lt;br /&gt;
                                                                                format:nil&lt;br /&gt;
                                                                                 error:&amp;amp;error];&lt;br /&gt;
    if (!dictionaryParsed)&lt;br /&gt;
    {&lt;br /&gt;
        if (error)&lt;br /&gt;
        {&lt;br /&gt;
            NSLog(@&amp;quot;%@&amp;quot;, [error localizedDescription]);&lt;br /&gt;
        }&lt;br /&gt;
        return nil;&lt;br /&gt;
    }&lt;br /&gt;
    return dictionaryParsed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (NSDictionary *)dictionaryFromJSONData:(NSData *)data&lt;br /&gt;
{&lt;br /&gt;
    NSError *error;&lt;br /&gt;
    NSDictionary *dictionaryParsed = [NSJSONSerialization JSONObjectWithData:data&lt;br /&gt;
                                                                     options:0&lt;br /&gt;
                                                                       error:&amp;amp;error];&lt;br /&gt;
    if (!dictionaryParsed)&lt;br /&gt;
    {&lt;br /&gt;
        if (error)&lt;br /&gt;
        {&lt;br /&gt;
            NSLog(@&amp;quot;%@&amp;quot;, [error localizedDescription]);&lt;br /&gt;
        }&lt;br /&gt;
        return nil;&lt;br /&gt;
    }&lt;br /&gt;
    return dictionaryParsed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#pragma mark Receipt Verification&lt;br /&gt;
&lt;br /&gt;
// This method should be called once a transaction gets to the SKPaymentTransactionStatePurchased or SKPaymentTransactionStateRestored state&lt;br /&gt;
// Call it with the SKPaymentTransaction.transactionReceipt&lt;br /&gt;
- (BOOL)verifyPurchase:(SKPaymentTransaction *)transaction&lt;br /&gt;
{&lt;br /&gt;
    BOOL isOk = [self isTransactionAndItsReceiptValid:transaction];&lt;br /&gt;
    if (!isOk)&lt;br /&gt;
    {&lt;br /&gt;
        // There was something wrong with the transaction we got back, so no need to call verifyReceipt.&lt;br /&gt;
        return isOk;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // The transaction looks ok, so start the verify process.&lt;br /&gt;
    &lt;br /&gt;
    // Encode the receiptData for the itms receipt verification POST request.&lt;br /&gt;
    NSString *jsonObjectString = [self encodeBase64:(uint8_t *)transaction.transactionReceipt.bytes&lt;br /&gt;
                                             length:transaction.transactionReceipt.length];&lt;br /&gt;
    &lt;br /&gt;
    // Create the POST request payload.&lt;br /&gt;
    NSString *payload = [NSString stringWithFormat:@&amp;quot;{\&amp;quot;receipt-data\&amp;quot; : \&amp;quot;%@\&amp;quot;, \&amp;quot;password\&amp;quot; : \&amp;quot;%@\&amp;quot;}&amp;quot;,&lt;br /&gt;
                         jsonObjectString, ITC_CONTENT_PROVIDER_SHARED_SECRET];&lt;br /&gt;
    &lt;br /&gt;
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];&lt;br /&gt;
    &lt;br /&gt;
#warning Check for the correct itms verify receipt URL&lt;br /&gt;
    // Use ITMS_SANDBOX_VERIFY_RECEIPT_URL while testing against the sandbox.&lt;br /&gt;
    NSString *serverURL = ITMS_SANDBOX_VERIFY_RECEIPT_URL;&lt;br /&gt;
    &lt;br /&gt;
    // Create the POST request to the server.&lt;br /&gt;
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverURL]];&lt;br /&gt;
    [request setHTTPMethod:@&amp;quot;POST&amp;quot;];&lt;br /&gt;
    [request setHTTPBody:payloadData];&lt;br /&gt;
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];&lt;br /&gt;
    &lt;br /&gt;
    [conn start];&lt;br /&gt;
    &lt;br /&gt;
    // The transation receipt has not been validated yet.  That is done from the NSURLConnection callback.&lt;br /&gt;
    return isOk;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Check the validity of the receipt.  If it checks out then also ensure the transaction is something&lt;br /&gt;
// we haven&amp;#039;t seen before and then decode and save the purchaseInfo from the receipt for later receipt validation.&lt;br /&gt;
- (BOOL)isTransactionAndItsReceiptValid:(SKPaymentTransaction *)transaction&lt;br /&gt;
{&lt;br /&gt;
    if (!(transaction &amp;amp;&amp;amp; transaction.transactionReceipt &amp;amp;&amp;amp; [transaction.transactionReceipt length] &amp;gt; 0))&lt;br /&gt;
    {&lt;br /&gt;
        // Transaction is not valid.&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Pull the purchase-info out of the transaction receipt, decode it, and save it for later so&lt;br /&gt;
    // it can be cross checked with the verifyReceipt.&lt;br /&gt;
    NSDictionary *receiptDict       = [self dictionaryFromPlistData:transaction.transactionReceipt];&lt;br /&gt;
    NSString *transactionPurchaseInfo = [receiptDict objectForKey:@&amp;quot;purchase-info&amp;quot;];&lt;br /&gt;
    NSString *decodedPurchaseInfo   = [self decodeBase64:transactionPurchaseInfo length:nil];&lt;br /&gt;
    NSDictionary *purchaseInfoDict  = [self dictionaryFromPlistData:[decodedPurchaseInfo dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;
    &lt;br /&gt;
    NSString *transactionId         = [purchaseInfoDict objectForKey:@&amp;quot;transaction-id&amp;quot;];&lt;br /&gt;
    NSString *purchaseDateString    = [purchaseInfoDict objectForKey:@&amp;quot;purchase-date&amp;quot;];&lt;br /&gt;
    NSString *signature             = [receiptDict objectForKey:@&amp;quot;signature&amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // Convert the string into a date&lt;br /&gt;
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];&lt;br /&gt;
    [dateFormat setDateFormat:@&amp;quot;yyyy-MM-dd HH:mm:ss z&amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    NSDate *purchaseDate = [dateFormat dateFromString:[purchaseDateString stringByReplacingOccurrencesOfString:@&amp;quot;Etc/&amp;quot; withString:@&amp;quot;&amp;quot;]];&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    if (![self isTransactionIdUnique:transactionId])&lt;br /&gt;
    {&lt;br /&gt;
        // We&amp;#039;ve seen this transaction before.&lt;br /&gt;
        // Had [transactionsReceiptStorageDictionary objectForKey:transactionId]&lt;br /&gt;
        // Got purchaseInfoDict&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Check the authenticity of the receipt response/signature etc.&lt;br /&gt;
&lt;br /&gt;
    BOOL result = checkReceiptSecurity(transactionPurchaseInfo, signature,&lt;br /&gt;
                                       (__bridge CFDateRef)(purchaseDate));&lt;br /&gt;
    &lt;br /&gt;
    if (!result)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Ensure the transaction itself is legit&lt;br /&gt;
    if (![self doTransactionDetailsMatchPurchaseInfo:transaction withPurchaseInfo:purchaseInfoDict])&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Make a note of the fact that we&amp;#039;ve seen the transaction id already&lt;br /&gt;
    [self saveTransactionId:transactionId];&lt;br /&gt;
    &lt;br /&gt;
    // Save the transaction receipt&amp;#039;s purchaseInfo in the transactionsReceiptStorageDictionary.&lt;br /&gt;
    [transactionsReceiptStorageDictionary setObject:purchaseInfoDict forKey:transactionId];&lt;br /&gt;
    &lt;br /&gt;
    return YES;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Make sure the transaction details actually match the purchase info&lt;br /&gt;
- (BOOL)doTransactionDetailsMatchPurchaseInfo:(SKPaymentTransaction *)transaction withPurchaseInfo:(NSDictionary *)purchaseInfoDict&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    if (!transaction || !purchaseInfoDict)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    int failCount = 0;&lt;br /&gt;
    &lt;br /&gt;
    if (![transaction.payment.productIdentifier isEqualToString:[purchaseInfoDict objectForKey:@&amp;quot;product-id&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        &lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (transaction.payment.quantity != [[purchaseInfoDict objectForKey:@&amp;quot;quantity&amp;quot;] intValue])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (![transaction.transactionIdentifier isEqualToString:[purchaseInfoDict objectForKey:@&amp;quot;transaction-id&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Optionally check the bid and bvrs match this app&amp;#039;s current bundle ID and bundle version.&lt;br /&gt;
    // Optionally check the requestData.&lt;br /&gt;
    // Optionally check the dates.&lt;br /&gt;
    &lt;br /&gt;
    if (failCount != 0)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // The transaction and its signed content seem ok.&lt;br /&gt;
    return YES;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (BOOL)isTransactionIdUnique:(NSString *)transactionId&lt;br /&gt;
{&lt;br /&gt;
    NSString *transactionDictionary = KNOWN_TRANSACTIONS_KEY;&lt;br /&gt;
    // Save the transactionId to the standardUserDefaults so we can check against that later&lt;br /&gt;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];&lt;br /&gt;
    [defaults synchronize];&lt;br /&gt;
    &lt;br /&gt;
    if (![defaults objectForKey:transactionDictionary])&lt;br /&gt;
    {&lt;br /&gt;
        [defaults setObject:[[NSMutableDictionary alloc] init] forKey:transactionDictionary];&lt;br /&gt;
        [defaults synchronize];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (![[defaults objectForKey:transactionDictionary] objectForKey:transactionId])&lt;br /&gt;
    {&lt;br /&gt;
        return YES;&lt;br /&gt;
    }&lt;br /&gt;
    // The transaction already exists in the defaults.&lt;br /&gt;
    return NO;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (void)saveTransactionId:(NSString *)transactionId&lt;br /&gt;
{&lt;br /&gt;
    // Save the transactionId to the standardUserDefaults so we can check against that later&lt;br /&gt;
    // If dictionary exists already then retrieve it and add new transactionID&lt;br /&gt;
    // Regardless save transactionID to dictionary which gets saved to NSUserDefaults&lt;br /&gt;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];&lt;br /&gt;
    NSString *transactionDictionary = KNOWN_TRANSACTIONS_KEY;&lt;br /&gt;
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:&lt;br /&gt;
                                       [defaults objectForKey:transactionDictionary]];&lt;br /&gt;
    if (!dictionary)&lt;br /&gt;
    {&lt;br /&gt;
        dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:1], transactionId, nil];&lt;br /&gt;
    } else {&lt;br /&gt;
        [dictionary setObject:[NSNumber numberWithInt:1] forKey:transactionId];&lt;br /&gt;
    }&lt;br /&gt;
    [defaults setObject:dictionary forKey:transactionDictionary];&lt;br /&gt;
    [defaults synchronize];&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (BOOL)doesTransactionInfoMatchReceipt:(NSString*) receiptString&lt;br /&gt;
{&lt;br /&gt;
    // Convert the responseString into a dictionary and pull out the receipt data.&lt;br /&gt;
    NSDictionary *verifiedReceiptDictionary = [self dictionaryFromJSONData:[receiptString dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;
    &lt;br /&gt;
    // Check the status of the verifyReceipt call&lt;br /&gt;
    id status = [verifiedReceiptDictionary objectForKey:@&amp;quot;status&amp;quot;];&lt;br /&gt;
    if (!status)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    int verifyReceiptStatus = [status integerValue];&lt;br /&gt;
    // 21006 = This receipt is valid but the subscription has expired.&lt;br /&gt;
    if (0 != verifyReceiptStatus &amp;amp;&amp;amp; 21006 != verifyReceiptStatus)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // The receipt is valid, so checked the receipt specifics now.&lt;br /&gt;
    &lt;br /&gt;
    NSDictionary *verifiedReceiptReceiptDictionary  = [verifiedReceiptDictionary objectForKey:@&amp;quot;receipt&amp;quot;];&lt;br /&gt;
    NSString *verifiedReceiptUniqueIdentifier       = [verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;unique_identifier&amp;quot;];&lt;br /&gt;
    NSString *transactionIdFromVerifiedReceipt      = [verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;transaction_id&amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // Get the transaction&amp;#039;s receipt data from the transactionsReceiptStorageDictionary&lt;br /&gt;
    NSDictionary *purchaseInfoFromTransaction = [transactionsReceiptStorageDictionary objectForKey:transactionIdFromVerifiedReceipt];&lt;br /&gt;
    &lt;br /&gt;
    if (!purchaseInfoFromTransaction)&lt;br /&gt;
    {&lt;br /&gt;
        // We didn&amp;#039;t find a receipt for this transaction.&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    // NOTE: Instead of counting errors you could just return early.&lt;br /&gt;
    int failCount = 0;&lt;br /&gt;
    &lt;br /&gt;
    // Verify all the receipt specifics to ensure everything matches up as expected&lt;br /&gt;
    if (![[verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;bid&amp;quot;]&lt;br /&gt;
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@&amp;quot;bid&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (![[verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;product_id&amp;quot;]&lt;br /&gt;
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@&amp;quot;product-id&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (![[verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;quantity&amp;quot;]&lt;br /&gt;
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@&amp;quot;quantity&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (![[verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;item_id&amp;quot;]&lt;br /&gt;
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@&amp;quot;item-id&amp;quot;]])&lt;br /&gt;
    {&lt;br /&gt;
        failCount++;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if ([[UIDevice currentDevice] respondsToSelector:NSSelectorFromString(@&amp;quot;identifierForVendor&amp;quot;)]) // iOS 6?&lt;br /&gt;
    {&lt;br /&gt;
#if IS_IOS6_AWARE&lt;br /&gt;
        // iOS 6 (or later)&lt;br /&gt;
        NSString *localIdentifier                   = [[[UIDevice currentDevice] identifierForVendor] UUIDString];&lt;br /&gt;
        NSString *purchaseInfoUniqueVendorId        = [purchaseInfoFromTransaction objectForKey:@&amp;quot;unique-vendor-identifier&amp;quot;];&lt;br /&gt;
        NSString *verifiedReceiptVendorIdentifier   = [verifiedReceiptReceiptDictionary objectForKey:@&amp;quot;unique_vendor_identifier&amp;quot;];&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        if(verifiedReceiptVendorIdentifier)&lt;br /&gt;
        {&lt;br /&gt;
            if (![purchaseInfoUniqueVendorId isEqualToString:verifiedReceiptVendorIdentifier]&lt;br /&gt;
                || ![purchaseInfoUniqueVendorId isEqualToString:localIdentifier])&lt;br /&gt;
            {&lt;br /&gt;
                // Comment this line out to test in the Simulator.&lt;br /&gt;
                failCount++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
#endif&lt;br /&gt;
    } else {&lt;br /&gt;
        // Pre iOS 6 &lt;br /&gt;
        NSString *localIdentifier           = [UIDevice currentDevice].uniqueIdentifier;&lt;br /&gt;
        NSString *purchaseInfoUniqueId      = [purchaseInfoFromTransaction objectForKey:@&amp;quot;unique-identifier&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        if (![purchaseInfoUniqueId isEqualToString:verifiedReceiptUniqueIdentifier]&lt;br /&gt;
            || ![purchaseInfoUniqueId isEqualToString:localIdentifier])&lt;br /&gt;
        {&lt;br /&gt;
            // Comment this line out to test in the Simulator.&lt;br /&gt;
            failCount++;&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    // Do addition time checks for the transaction and receipt.&lt;br /&gt;
    &lt;br /&gt;
    if(failCount != 0)&lt;br /&gt;
    {&lt;br /&gt;
        return NO;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return YES;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#pragma mark NSURLConnectionDelegate (for the verifyReceipt connection)&lt;br /&gt;
&lt;br /&gt;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data&lt;br /&gt;
{&lt;br /&gt;
    NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];&lt;br /&gt;
    &lt;br /&gt;
    // So we got some receipt data. Now does it all check out?&lt;br /&gt;
    BOOL isOk = [self doesTransactionInfoMatchReceipt:responseString];&lt;br /&gt;
    &lt;br /&gt;
    if (isOk)&lt;br /&gt;
    {&lt;br /&gt;
        //Validation suceeded. Unlock content here.&lt;br /&gt;
#warning Validation suceeded. Unlock content here.&lt;br /&gt;
        NSLog(@&amp;quot;finally fucking passed&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge&lt;br /&gt;
{&lt;br /&gt;
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])&lt;br /&gt;
    {&lt;br /&gt;
        SecTrustRef trust = [[challenge protectionSpace] serverTrust];&lt;br /&gt;
        NSError *error = nil;&lt;br /&gt;
        BOOL didUseCredential = NO;&lt;br /&gt;
        BOOL isTrusted = [self validateTrust:trust error:&amp;amp;error];&lt;br /&gt;
        if (isTrusted)&lt;br /&gt;
        {&lt;br /&gt;
            NSURLCredential *trust_credential = [NSURLCredential credentialForTrust:trust];&lt;br /&gt;
            if (trust_credential)&lt;br /&gt;
            {&lt;br /&gt;
                [[challenge sender] useCredential:trust_credential forAuthenticationChallenge:challenge];&lt;br /&gt;
                didUseCredential = YES;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (!didUseCredential)&lt;br /&gt;
        {&lt;br /&gt;
            [[challenge sender] cancelAuthenticationChallenge:challenge];&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        [[challenge sender] performDefaultHandlingForAuthenticationChallenge:challenge];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// NOTE: These are needed for 4.x (as willSendRequestForAuthenticationChallenge: is not supported)&lt;br /&gt;
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace&lt;br /&gt;
{&lt;br /&gt;
    return [[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge&lt;br /&gt;
{&lt;br /&gt;
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])&lt;br /&gt;
    {&lt;br /&gt;
        SecTrustRef trust = [[challenge protectionSpace] serverTrust];&lt;br /&gt;
        NSError *error = nil;&lt;br /&gt;
        BOOL didUseCredential = NO;&lt;br /&gt;
        BOOL isTrusted = [self validateTrust:trust error:&amp;amp;error];&lt;br /&gt;
        if (isTrusted)&lt;br /&gt;
        {&lt;br /&gt;
            NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];&lt;br /&gt;
            if (credential)&lt;br /&gt;
            {&lt;br /&gt;
                [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];&lt;br /&gt;
                didUseCredential = YES;&lt;br /&gt;
            }&lt;br /&gt;
		}&lt;br /&gt;
        if (! didUseCredential) {&lt;br /&gt;
            [[challenge sender] cancelAuthenticationChallenge:challenge];&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        [[challenge sender] performDefaultHandlingForAuthenticationChallenge:challenge];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#pragma mark&lt;br /&gt;
#pragma mark NSURLConnection - Trust validation&lt;br /&gt;
&lt;br /&gt;
- (BOOL)validateTrust:(SecTrustRef)trust error:(NSError **)error&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
    // Include some Security framework SPIs&lt;br /&gt;
    extern CFStringRef kSecTrustInfoExtendedValidationKey;&lt;br /&gt;
    extern CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust);&lt;br /&gt;
    &lt;br /&gt;
    BOOL trusted = NO;&lt;br /&gt;
    SecTrustResultType trust_result;&lt;br /&gt;
    if ((noErr == SecTrustEvaluate(trust, &amp;amp;trust_result)) &amp;amp;&amp;amp; (trust_result == kSecTrustResultUnspecified))&lt;br /&gt;
    {&lt;br /&gt;
        NSDictionary *trust_info = (__bridge_transfer NSDictionary *)SecTrustCopyInfo(trust);&lt;br /&gt;
        id hasEV = [trust_info objectForKey:(__bridge NSString *)kSecTrustInfoExtendedValidationKey];&lt;br /&gt;
        trusted =  [hasEV isKindOfClass:[NSValue class]] &amp;amp;&amp;amp; [hasEV boolValue];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (trust)&lt;br /&gt;
    {&lt;br /&gt;
        if (!trusted &amp;amp;&amp;amp; error)&lt;br /&gt;
        {&lt;br /&gt;
            *error = [NSError errorWithDomain:@&amp;quot;kSecTrustError&amp;quot; code:(NSInteger)trust_result userInfo:nil];&lt;br /&gt;
        }&lt;br /&gt;
        return trusted;&lt;br /&gt;
    }&lt;br /&gt;
    return NO;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#pragma mark&lt;br /&gt;
#pragma mark Check Receipt signature&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;CommonCrypto/CommonDigest.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Security/Security.h&amp;gt;&lt;br /&gt;
#include &amp;lt;AssertMacros.h&amp;gt;&lt;br /&gt;
unsigned int iTS_intermediate_der_len = 1039;&lt;br /&gt;
&lt;br /&gt;
unsigned char iTS_intermediate_der[] = {&lt;br /&gt;
    0x30, 0x82, 0x04, 0x0b, 0x30, 0x82, 0x02, 0xf3, 0xa0, 0x03, 0x02, 0x01,&lt;br /&gt;
    0x02, 0x02, 0x01, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,&lt;br /&gt;
    0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x62, 0x31, 0x0b, 0x30,&lt;br /&gt;
    0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13,&lt;br /&gt;
    0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70,&lt;br /&gt;
    0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06,&lt;br /&gt;
    0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20,&lt;br /&gt;
    0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,&lt;br /&gt;
    0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,&lt;br /&gt;
    0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x41, 0x70,&lt;br /&gt;
    0x70, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30,&lt;br /&gt;
    0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x35, 0x31, 0x39, 0x31, 0x38, 0x33,&lt;br /&gt;
    0x31, 0x33, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x35, 0x31, 0x38,&lt;br /&gt;
    0x31, 0x38, 0x33, 0x31, 0x33, 0x30, 0x5a, 0x30, 0x7f, 0x31, 0x0b, 0x30,&lt;br /&gt;
    0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13,&lt;br /&gt;
    0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70,&lt;br /&gt;
    0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06,&lt;br /&gt;
    0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20,&lt;br /&gt;
    0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,&lt;br /&gt;
    0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,&lt;br /&gt;
    0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2a, 0x41, 0x70,&lt;br /&gt;
    0x70, 0x6c, 0x65, 0x20, 0x69, 0x54, 0x75, 0x6e, 0x65, 0x73, 0x20, 0x53,&lt;br /&gt;
    0x74, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,&lt;br /&gt;
    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,&lt;br /&gt;
    0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,&lt;br /&gt;
    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,&lt;br /&gt;
    0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,&lt;br /&gt;
    0x00, 0xa4, 0xbc, 0xaf, 0x32, 0x94, 0x43, 0x3e, 0x0b, 0xbc, 0x37, 0x87,&lt;br /&gt;
    0xcd, 0x63, 0x89, 0xf2, 0xcc, 0xd9, 0xbe, 0x20, 0x4d, 0x5a, 0xb4, 0xfe,&lt;br /&gt;
    0x87, 0x67, 0xd2, 0x9a, 0xde, 0x1a, 0x54, 0x9d, 0xa2, 0xf3, 0xdf, 0x87,&lt;br /&gt;
    0xe4, 0x4c, 0xcb, 0x93, 0x11, 0x78, 0xa0, 0x30, 0x8f, 0x34, 0x41, 0xc1,&lt;br /&gt;
    0xd3, 0xbe, 0x66, 0x6d, 0x47, 0x6c, 0x98, 0xb8, 0xec, 0x7a, 0xd5, 0xc9,&lt;br /&gt;
    0xdd, 0xa5, 0xe4, 0xea, 0xc6, 0x70, 0xf4, 0x35, 0xd0, 0x91, 0xf7, 0xb3,&lt;br /&gt;
    0xd8, 0x0a, 0x11, 0x99, 0xab, 0x3a, 0x62, 0x3a, 0xbd, 0x7b, 0xf4, 0x56,&lt;br /&gt;
    0x4f, 0xdb, 0x9f, 0x24, 0x93, 0x51, 0x50, 0x7c, 0x20, 0xd5, 0x66, 0x4d,&lt;br /&gt;
    0x66, 0xf3, 0x18, 0xa4, 0x13, 0x96, 0x22, 0x16, 0xfd, 0x31, 0xa7, 0xf4,&lt;br /&gt;
    0x39, 0x66, 0x9b, 0xfb, 0x62, 0x69, 0x5c, 0x4b, 0x9f, 0x94, 0xa8, 0x4b,&lt;br /&gt;
    0xe8, 0xec, 0x5b, 0x64, 0x5a, 0x18, 0x79, 0x8a, 0x16, 0x75, 0x63, 0x42,&lt;br /&gt;
    0xa4, 0x49, 0xd9, 0x8c, 0x33, 0xde, 0xad, 0x7b, 0xd6, 0x39, 0x04, 0xf4,&lt;br /&gt;
    0xe2, 0x9d, 0x0a, 0x69, 0x8c, 0xeb, 0x4b, 0x12, 0x28, 0x4b, 0x34, 0x48,&lt;br /&gt;
    0x07, 0x9b, 0x0e, 0x59, 0xf9, 0x1f, 0x62, 0xb0, 0x03, 0x9f, 0x36, 0xb8,&lt;br /&gt;
    0x4e, 0xa3, 0xd3, 0x75, 0x59, 0xd4, 0xf3, 0x3a, 0x05, 0xca, 0xc5, 0x33,&lt;br /&gt;
    0x3b, 0xf8, 0xc0, 0x06, 0x09, 0x08, 0x93, 0xdb, 0xe7, 0x4d, 0xbf, 0x11,&lt;br /&gt;
    0xf3, 0x52, 0x2c, 0xa5, 0x16, 0x35, 0x15, 0xf3, 0x41, 0x02, 0xcd, 0x02,&lt;br /&gt;
    0xd1, 0xfc, 0xf5, 0xf8, 0xc5, 0x84, 0xbd, 0x63, 0x6a, 0x86, 0xd6, 0xb6,&lt;br /&gt;
    0x99, 0xf6, 0x86, 0xae, 0x5f, 0xfd, 0x03, 0xd4, 0x28, 0x8a, 0x5a, 0x5d,&lt;br /&gt;
    0xaf, 0xbc, 0x65, 0x74, 0xd1, 0xf7, 0x1a, 0xc3, 0x92, 0x08, 0xf4, 0x1c,&lt;br /&gt;
    0xad, 0x69, 0xe8, 0x02, 0x4c, 0x0e, 0x95, 0x15, 0x07, 0xbc, 0xbe, 0x6a,&lt;br /&gt;
    0x6f, 0xc1, 0xb3, 0xad, 0xa1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,&lt;br /&gt;
    0xae, 0x30, 0x81, 0xab, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,&lt;br /&gt;
    0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03,&lt;br /&gt;
    0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,&lt;br /&gt;
    0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,&lt;br /&gt;
    0x36, 0x1d, 0xe8, 0xe2, 0x9d, 0x82, 0xd2, 0x01, 0x18, 0xb5, 0x32, 0x6b,&lt;br /&gt;
    0x0e, 0xd7, 0x43, 0x0b, 0x91, 0x58, 0x43, 0x3a, 0x30, 0x1f, 0x06, 0x03,&lt;br /&gt;
    0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x2b, 0xd0, 0x69,&lt;br /&gt;
    0x47, 0x94, 0x76, 0x09, 0xfe, 0xf4, 0x6b, 0x8d, 0x2e, 0x40, 0xa6, 0xf7,&lt;br /&gt;
    0x47, 0x4d, 0x7f, 0x08, 0x5e, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f,&lt;br /&gt;
    0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25,&lt;br /&gt;
    0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61,&lt;br /&gt;
    0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x70,&lt;br /&gt;
    0x6c, 0x65, 0x63, 0x61, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,&lt;br /&gt;
    0x6c, 0x30, 0x10, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64,&lt;br /&gt;
    0x06, 0x02, 0x02, 0x04, 0x02, 0x05, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,&lt;br /&gt;
    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,&lt;br /&gt;
    0x01, 0x01, 0x00, 0x75, 0xa6, 0x90, 0xe6, 0x9a, 0xa7, 0xdb, 0x65, 0x70,&lt;br /&gt;
    0xa6, 0x09, 0x93, 0x6f, 0x08, 0xdf, 0x2c, 0xdb, 0xe9, 0x28, 0x8d, 0x40,&lt;br /&gt;
    0x1b, 0x57, 0x5e, 0xa0, 0xea, 0xf4, 0xec, 0x13, 0x65, 0x1b, 0x71, 0x4a,&lt;br /&gt;
    0x4d, 0xdc, 0x80, 0x48, 0x4f, 0xf2, 0xe5, 0xa9, 0xfb, 0x85, 0x6c, 0xb7,&lt;br /&gt;
    0x1e, 0x9d, 0xdb, 0xf4, 0x18, 0x48, 0x10, 0x79, 0x17, 0xea, 0xc3, 0x3d,&lt;br /&gt;
    0x87, 0xd8, 0xb4, 0x79, 0x6d, 0x14, 0x50, 0xad, 0xd2, 0xbf, 0x3d, 0x4e,&lt;br /&gt;
    0xfc, 0x0d, 0xe2, 0xc5, 0x03, 0x94, 0x75, 0x80, 0x73, 0x4d, 0xa5, 0xa1,&lt;br /&gt;
    0x91, 0xfe, 0x1c, 0xde, 0x15, 0x17, 0xac, 0x89, 0x71, 0x2a, 0x6f, 0x0f,&lt;br /&gt;
    0x67, 0x0a, 0xd3, 0x9c, 0x30, 0xa1, 0x68, 0xfb, 0xcf, 0x70, 0x17, 0xca,&lt;br /&gt;
    0xd9, 0x40, 0xfc, 0xf8, 0x1b, 0xbf, 0xce, 0xb0, 0xc4, 0xae, 0xf4, 0x4a,&lt;br /&gt;
    0x2d, 0xa9, 0x99, 0x87, 0x06, 0x42, 0x09, 0x86, 0x22, 0x6a, 0x84, 0x40,&lt;br /&gt;
    0x39, 0xf4, 0xbb, 0xac, 0x56, 0x18, 0xf7, 0x9a, 0x1c, 0x01, 0x81, 0x5c,&lt;br /&gt;
    0x8c, 0x6e, 0x41, 0xf2, 0x5d, 0x19, 0x2c, 0x17, 0x1c, 0x49, 0x46, 0xd9,&lt;br /&gt;
    0x1c, 0x7e, 0x93, 0x12, 0x13, 0xc8, 0x67, 0x99, 0xc2, 0xea, 0x83, 0xe3,&lt;br /&gt;
    0xa2, 0x8c, 0x0e, 0xb8, 0x3b, 0x2a, 0xdf, 0x1c, 0xbf, 0x4b, 0x8b, 0x6f,&lt;br /&gt;
    0x1a, 0xb8, 0xee, 0x97, 0x67, 0x4a, 0xd8, 0xab, 0xaf, 0x8b, 0xa4, 0xda,&lt;br /&gt;
    0x5c, 0x87, 0x1e, 0x20, 0xb8, 0xc5, 0xf3, 0xb1, 0xc4, 0x98, 0xa2, 0x37,&lt;br /&gt;
    0xf8, 0x9e, 0xc6, 0x9a, 0x6b, 0xa5, 0xad, 0xf6, 0x78, 0x96, 0x0e, 0x82,&lt;br /&gt;
    0x8f, 0x04, 0x46, 0x1c, 0xb2, 0xa5, 0xfd, 0x9a, 0x30, 0x51, 0x28, 0xfd,&lt;br /&gt;
    0x52, 0x04, 0x15, 0x03, 0xd5, 0x3c, 0xad, 0xfe, 0xf6, 0x78, 0xe0, 0xea,&lt;br /&gt;
    0x35, 0xef, 0x65, 0xb5, 0x21, 0x76, 0xdb, 0xa4, 0xef, 0xcb, 0x72, 0xef,&lt;br /&gt;
    0x54, 0x6b, 0x01, 0x0d, 0xc7, 0xdd, 0x1a&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BOOL checkReceiptSecurity(NSString *purchase_info_string, NSString *signature_string, CFDateRef purchaseDate)&lt;br /&gt;
{&lt;br /&gt;
    BOOL valid = NO;&lt;br /&gt;
    SecCertificateRef leaf = NULL, intermediate = NULL;&lt;br /&gt;
    SecTrustRef trust = NULL;&lt;br /&gt;
    SecPolicyRef policy = SecPolicyCreateBasicX509();&lt;br /&gt;
    &lt;br /&gt;
    NSData *certificate_data;&lt;br /&gt;
    NSArray *anchors;&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Parse inputs:&lt;br /&gt;
     purchase_info_string and signature_string are base64 encoded JSON blobs that need to&lt;br /&gt;
     be decoded.&lt;br /&gt;
     */&lt;br /&gt;
    &lt;br /&gt;
    require([purchase_info_string canBeConvertedToEncoding:NSASCIIStringEncoding] &amp;amp;&amp;amp;&lt;br /&gt;
            [signature_string canBeConvertedToEncoding:NSASCIIStringEncoding], outLabel);&lt;br /&gt;
    &lt;br /&gt;
    size_t purchase_info_length;&lt;br /&gt;
    uint8_t *purchase_info_bytes = base64_decode([purchase_info_string cStringUsingEncoding:NSASCIIStringEncoding],&lt;br /&gt;
                                                 &amp;amp;purchase_info_length);&lt;br /&gt;
    &lt;br /&gt;
    size_t signature_length;&lt;br /&gt;
    uint8_t *signature_bytes = base64_decode([signature_string cStringUsingEncoding:NSASCIIStringEncoding],&lt;br /&gt;
                                             &amp;amp;signature_length);&lt;br /&gt;
    &lt;br /&gt;
    require(purchase_info_bytes &amp;amp;&amp;amp; signature_bytes, outLabel);&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Binary format looks as follows:&lt;br /&gt;
     &lt;br /&gt;
     RECEIPTVERSION | SIGNATURE | CERTIFICATE SIZE | CERTIFICATE&lt;br /&gt;
     1 byte           128         4 bytes&lt;br /&gt;
     big endian&lt;br /&gt;
     &lt;br /&gt;
     Extract version, signature and certificate(s).&lt;br /&gt;
     Check receipt version == 2.&lt;br /&gt;
     Sanity check that signature is 128 bytes.&lt;br /&gt;
     Sanity check certificate size &amp;lt;= remaining payload data.&lt;br /&gt;
     */&lt;br /&gt;
    &lt;br /&gt;
#pragma pack(push, 1)&lt;br /&gt;
    struct signature_blob {&lt;br /&gt;
        uint8_t version;&lt;br /&gt;
        uint8_t signature[128];&lt;br /&gt;
        uint32_t cert_len;&lt;br /&gt;
        uint8_t certificate[];&lt;br /&gt;
    } *signature_blob_ptr = (struct signature_blob *)signature_bytes;&lt;br /&gt;
#pragma pack(pop)&lt;br /&gt;
    uint32_t certificate_len;&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Make sure the signature blob is long enough to safely extract the version and&lt;br /&gt;
     cert_len fields, then perform a sanity check on the fields.&lt;br /&gt;
     */&lt;br /&gt;
    require(signature_length &amp;gt; offsetof(struct signature_blob, certificate), outLabel);&lt;br /&gt;
    require(signature_blob_ptr-&amp;gt;version == 2, outLabel);&lt;br /&gt;
    certificate_len = ntohl(signature_blob_ptr-&amp;gt;cert_len);&lt;br /&gt;
    &lt;br /&gt;
    require(signature_length - offsetof(struct signature_blob, certificate) &amp;gt;= certificate_len, outLabel);&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Validate certificate chains back to valid receipt signer; policy approximation for now&lt;br /&gt;
     set intermediate as a trust anchor; current intermediate lapses in 2016.&lt;br /&gt;
     */&lt;br /&gt;
    &lt;br /&gt;
    certificate_data = [NSData dataWithBytes:signature_blob_ptr-&amp;gt;certificate length:certificate_len];&lt;br /&gt;
    require(leaf = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certificate_data), outLabel);&lt;br /&gt;
    &lt;br /&gt;
    certificate_data = [NSData dataWithBytes:iTS_intermediate_der length:iTS_intermediate_der_len];&lt;br /&gt;
    require(intermediate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certificate_data), outLabel);&lt;br /&gt;
    &lt;br /&gt;
    anchors = [NSArray arrayWithObject:(__bridge id)intermediate];&lt;br /&gt;
    require(anchors, outLabel);&lt;br /&gt;
    &lt;br /&gt;
    require_noerr(SecTrustCreateWithCertificates(leaf, policy, &amp;amp;trust), outLabel);&lt;br /&gt;
    require_noerr(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef) anchors), outLabel);&lt;br /&gt;
    &lt;br /&gt;
    if (purchaseDate)&lt;br /&gt;
    {&lt;br /&gt;
        require_noerr(SecTrustSetVerifyDate(trust, purchaseDate), outLabel);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    SecTrustResultType trust_result;&lt;br /&gt;
    require_noerr(SecTrustEvaluate(trust, &amp;amp;trust_result), outLabel);&lt;br /&gt;
    require(trust_result == kSecTrustResultUnspecified, outLabel);&lt;br /&gt;
    &lt;br /&gt;
    require(2 == SecTrustGetCertificateCount(trust), outLabel);&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Chain is valid, use leaf key to verify signature on receipt by&lt;br /&gt;
     calculating SHA1(version|purchaseInfo)&lt;br /&gt;
     */&lt;br /&gt;
    &lt;br /&gt;
    CC_SHA1_CTX sha1_ctx;&lt;br /&gt;
    uint8_t to_be_verified_data[CC_SHA1_DIGEST_LENGTH];&lt;br /&gt;
    &lt;br /&gt;
    CC_SHA1_Init(&amp;amp;sha1_ctx);&lt;br /&gt;
    CC_SHA1_Update(&amp;amp;sha1_ctx, &amp;amp;signature_blob_ptr-&amp;gt;version, sizeof(signature_blob_ptr-&amp;gt;version));&lt;br /&gt;
    CC_SHA1_Update(&amp;amp;sha1_ctx, purchase_info_bytes, purchase_info_length);&lt;br /&gt;
    CC_SHA1_Final(to_be_verified_data, &amp;amp;sha1_ctx);&lt;br /&gt;
    &lt;br /&gt;
    SecKeyRef receipt_signing_key = SecTrustCopyPublicKey(trust);&lt;br /&gt;
    require(receipt_signing_key, outLabel);&lt;br /&gt;
    require_noerr(SecKeyRawVerify(receipt_signing_key, kSecPaddingPKCS1SHA1,&lt;br /&gt;
                                  to_be_verified_data, sizeof(to_be_verified_data),&lt;br /&gt;
                                  signature_blob_ptr-&amp;gt;signature, sizeof(signature_blob_ptr-&amp;gt;signature)),&lt;br /&gt;
                  outLabel);&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     Optional:  Verify that the receipt certificate has the 1.2.840.113635.100.6.5.1 Null OID&lt;br /&gt;
     &lt;br /&gt;
     The signature is a 1024-bit RSA signature.&lt;br /&gt;
     */&lt;br /&gt;
    &lt;br /&gt;
    valid = YES;&lt;br /&gt;
    &lt;br /&gt;
outLabel:&lt;br /&gt;
    if (leaf) CFRelease(leaf);&lt;br /&gt;
    if (intermediate) CFRelease(intermediate);&lt;br /&gt;
    if (trust) CFRelease(trust);&lt;br /&gt;
    if (policy) CFRelease(policy);&lt;br /&gt;
    &lt;br /&gt;
    return valid;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#pragma mark&lt;br /&gt;
#pragma mark Base 64 encoding&lt;br /&gt;
&lt;br /&gt;
//  single base64 character conversion&lt;br /&gt;
&lt;br /&gt;
static int POS(char c)&lt;br /&gt;
{&lt;br /&gt;
    if (c&amp;gt;=&amp;#039;A&amp;#039; &amp;amp;&amp;amp; c&amp;lt;=&amp;#039;Z&amp;#039;) return c - &amp;#039;A&amp;#039;;&lt;br /&gt;
    if (c&amp;gt;=&amp;#039;a&amp;#039; &amp;amp;&amp;amp; c&amp;lt;=&amp;#039;z&amp;#039;) return c - &amp;#039;a&amp;#039; + 26;&lt;br /&gt;
    if (c&amp;gt;=&amp;#039;0&amp;#039; &amp;amp;&amp;amp; c&amp;lt;=&amp;#039;9&amp;#039;) return c - &amp;#039;0&amp;#039; + 52;&lt;br /&gt;
    if (c == &amp;#039;+&amp;#039;) return 62;&lt;br /&gt;
    if (c == &amp;#039;/&amp;#039;) return 63;&lt;br /&gt;
    if (c == &amp;#039;=&amp;#039;) return -1;&lt;br /&gt;
    &lt;br /&gt;
    [NSException raise:@&amp;quot;invalid BASE64 encoding&amp;quot; format:@&amp;quot;Invalid BASE64 encoding&amp;quot;];&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
- (NSString *)encodeBase64:(const uint8_t *)input length:(NSInteger)length&lt;br /&gt;
{&lt;br /&gt;
    return [NSString stringWithUTF8String:base64_encode(input, (size_t)length)];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- (NSString *)decodeBase64:(NSString *)input length:(NSInteger *)length&lt;br /&gt;
{&lt;br /&gt;
    size_t retLen;&lt;br /&gt;
    uint8_t *retStr = base64_decode([input UTF8String], &amp;amp;retLen);&lt;br /&gt;
    if (length)&lt;br /&gt;
        *length = (NSInteger)retLen;&lt;br /&gt;
    NSString *st = [[[NSString alloc] initWithBytes:retStr&lt;br /&gt;
                                             length:retLen&lt;br /&gt;
                                           encoding:NSUTF8StringEncoding] autorelease];&lt;br /&gt;
    free(retStr);    // If base64_decode returns dynamically allocated memory&lt;br /&gt;
    return st;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
char* base64_encode(const void* buf, size_t size)&lt;br /&gt;
{&lt;br /&gt;
    static const char base64[] =  &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
    char* str = (char*) malloc((size+3)*4/3 + 1);&lt;br /&gt;
    &lt;br /&gt;
    char* p = str;&lt;br /&gt;
    unsigned char* q = (unsigned char*) buf;&lt;br /&gt;
    size_t i = 0;&lt;br /&gt;
    &lt;br /&gt;
    while(i &amp;lt; size) {&lt;br /&gt;
        int c = q[i++];&lt;br /&gt;
        c *= 256;&lt;br /&gt;
        if (i &amp;lt; size) c += q[i];&lt;br /&gt;
        i++;&lt;br /&gt;
        &lt;br /&gt;
        c *= 256;&lt;br /&gt;
        if (i &amp;lt; size) c += q[i];&lt;br /&gt;
        i++;&lt;br /&gt;
        &lt;br /&gt;
        *p++ = base64[(c &amp;amp; 0x00fc0000) &amp;gt;&amp;gt; 18];&lt;br /&gt;
        *p++ = base64[(c &amp;amp; 0x0003f000) &amp;gt;&amp;gt; 12];&lt;br /&gt;
        &lt;br /&gt;
        if (i &amp;gt; size + 1)&lt;br /&gt;
            *p++ = &amp;#039;=&amp;#039;;&lt;br /&gt;
        else&lt;br /&gt;
            *p++ = base64[(c &amp;amp; 0x00000fc0) &amp;gt;&amp;gt; 6];&lt;br /&gt;
        &lt;br /&gt;
        if (i &amp;gt; size)&lt;br /&gt;
            *p++ = &amp;#039;=&amp;#039;;&lt;br /&gt;
        else&lt;br /&gt;
            *p++ = base64[c &amp;amp; 0x0000003f];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    *p = 0;&lt;br /&gt;
    &lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void* base64_decode(const char* s, size_t* data_len_ptr)&lt;br /&gt;
{&lt;br /&gt;
    size_t len = strlen(s);&lt;br /&gt;
    &lt;br /&gt;
    if (len % 4)&lt;br /&gt;
        [NSException raise:@&amp;quot;Invalid input in base64_decode&amp;quot; format:@&amp;quot;%d is an invalid length for an input string for BASE64 decoding&amp;quot;, len];&lt;br /&gt;
    &lt;br /&gt;
    unsigned char* data = (unsigned char*) malloc(len/4*3);&lt;br /&gt;
    &lt;br /&gt;
    int n[4];&lt;br /&gt;
    unsigned char* q = (unsigned char*) data;&lt;br /&gt;
    &lt;br /&gt;
    for(const char*p=s; *p; )&lt;br /&gt;
    {&lt;br /&gt;
        n[0] = POS(*p++);&lt;br /&gt;
        n[1] = POS(*p++);&lt;br /&gt;
        n[2] = POS(*p++);&lt;br /&gt;
        n[3] = POS(*p++);&lt;br /&gt;
        &lt;br /&gt;
        if (n[0]==-1 || n[1]==-1)&lt;br /&gt;
            [NSException raise:@&amp;quot;Invalid input in base64_decode&amp;quot; format:@&amp;quot;Invalid BASE64 encoding&amp;quot;];&lt;br /&gt;
        &lt;br /&gt;
        if (n[2]==-1 &amp;amp;&amp;amp; n[3]!=-1)&lt;br /&gt;
            [NSException raise:@&amp;quot;Invalid input in base64_decode&amp;quot; format:@&amp;quot;Invalid BASE64 encoding&amp;quot;];&lt;br /&gt;
        &lt;br /&gt;
        q[0] = (n[0] &amp;lt;&amp;lt; 2) + (n[1] &amp;gt;&amp;gt; 4);&lt;br /&gt;
        if (n[2] != -1) q[1] = ((n[1] &amp;amp; 15) &amp;lt;&amp;lt; 4) + (n[2] &amp;gt;&amp;gt; 2);&lt;br /&gt;
        if (n[3] != -1) q[2] = ((n[2] &amp;amp; 3) &amp;lt;&amp;lt; 6) + n[3];&lt;br /&gt;
        q += 3;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // make sure that data_len_ptr is not null&lt;br /&gt;
    if (!data_len_ptr)&lt;br /&gt;
        [NSException raise:@&amp;quot;Invalid input in base64_decode&amp;quot; format:@&amp;quot;Invalid destination for output string length&amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    *data_len_ptr = q-data - (n[2]==-1) - (n[3]==-1);&lt;br /&gt;
    &lt;br /&gt;
    return data;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>	</entry>

	</feed>