【问题标题】:In app purchase receipt verification in iOS8iOS8中的应用内购买收据验证
【发布时间】:2014-11-12 15:19:08
【问题描述】:

我们正在关注 http://www.raywenderlich.com/23266/in-app-purchases-in-ios-6-tutorial-consumables-and-receipt-validation 的 raywenderlinch 教程

但是在我们的项目中导入验证控制器类时出现了一些错误。现在,我正在使用 iOS8。

Error is: Implicit declaration of function 'checkReciptSecurity' is invalid in C99

我也在苹果开发者网站上搜索过 Verification 类的示例代码,没有找到他们的页面。

请给我您的解决方案或提供iOS8更新的验证类链接。

【问题讨论】:

    标签: ios objective-c iphone in-app-purchase


    【解决方案1】:

    在 VerificationController.h 中这样放置函数原型:

    - (void)verifyPurchase:(SKPaymentTransaction *)transaction completionHandler:(VerifyCompletionHandler)completionHandler;
    BOOL checkReceiptSecurity(NSString *purchase_info_string, NSString *signature_string, CFDateRef purchaseDate);
    

    这样做的原因是调用函数checkReceiptSecurity的行号在函数声明之前。

    您必须修改 VerificationController.m 文件代码。 我已经把修改后的代码放在这里了。

    #import "VerificationController.h"
    #import "NSData+Base64.h"
    
    static VerificationController *singleton;
    
    @implementation VerificationController {
        NSMutableDictionary * _completionHandlers;
    }
    
    + (VerificationController *)sharedInstance
    {
        if (singleton == nil)
        {
            singleton = [[VerificationController alloc] init];
        }
     return singleton;
    }
    
    
     - (id)init
    {
    self = [super init];
    if (self != nil)
    {
        transactionsReceiptStorageDictionary = [[NSMutableDictionary alloc] init];
        _completionHandlers = [[NSMutableDictionary alloc] init];
       }
       return self;
    }
    
    
    - (NSDictionary *)dictionaryFromPlistData:(NSData *)data
    {
    NSError *error;
    NSDictionary *dictionaryParsed = [NSPropertyListSerialization propertyListWithData:data
                                                                               options:NSPropertyListImmutable
                                                                                format:nil
                                                                                 error:&error];
    if (!dictionaryParsed)
    {
        if (error)
        {
            NSLog(@"Error parsing plist");
        }
        return nil;
    }
    return dictionaryParsed;
    }
    
    
    - (NSDictionary *)dictionaryFromJSONData:(NSData *)data
    {
    NSError *error;
    NSDictionary *dictionaryParsed = [NSJSONSerialization JSONObjectWithData:data
                                                                     options:0
                                                                       error:&error];
    if (!dictionaryParsed)
    {
        if (error)
        {
            NSLog(@"Error parsing dictionary");
        }
        return nil;
    }
    return dictionaryParsed;
    }
    
    
    #pragma mark Receipt Verification
    
    // This method should be called once a transaction gets to the SKPaymentTransactionStatePurchased or    SKPaymentTransactionStateRestored state
    // Call it with the SKPaymentTransaction.transactionReceipt
    - (void)verifyPurchase:(SKPaymentTransaction *)transaction completionHandler:(VerifyCompletionHandler)completionHandler
    {    
    BOOL isOk = [self isTransactionAndItsReceiptValid:transaction];
    if (!isOk)
    {
        // There was something wrong with the transaction we got back, so no need to call verifyReceipt.
        NSLog(@"Invalid transacion");
        completionHandler(FALSE);
        return;
    }
    
    // The transaction looks ok, so start the verify process.
    
    // Encode the receiptData for the itms receipt verification POST request.
    NSString *jsonObjectString = [self encodeBase64:(uint8_t *)transaction.transactionReceipt.bytes
                                             length:transaction.transactionReceipt.length];
    
    // Create the POST request payload.
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\", \"password\" : \"%@\"}",
                         jsonObjectString, ITC_CONTENT_PROVIDER_SHARED_SECRET];
    
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    
    #warning Check for the correct itms verify receipt URL
    // Use ITMS_SANDBOX_VERIFY_RECEIPT_URL while testing against the sandbox.
    NSString *serverURL = ITMS_SANDBOX_VERIFY_RECEIPT_URL; //ITMS_PROD_VERIFY_RECEIPT_URL;
    
    // Create the POST request to the server.
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverURL]];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:payloadData];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    
    _completionHandlers[[NSValue valueWithNonretainedObject:conn]] = completionHandler;
    
    [conn start];
    
    // The transation receipt has not been validated yet.  That is done from the NSURLConnection callback.
    }
    
    // Check the validity of the receipt.  If it checks out then also ensure the transaction is something
    // we haven't seen before and then decode and save the purchaseInfo from the receipt for later receipt validation.
    - (BOOL)isTransactionAndItsReceiptValid:(SKPaymentTransaction *)transaction
    {
    if (!(transaction && transaction.transactionReceipt && [transaction.transactionReceipt length] > 0))
    {
        // Transaction is not valid.
        return NO;
    }
    
    // Pull the purchase-info out of the transaction receipt, decode it, and save it for later so
    // it can be cross checked with the verifyReceipt.
    NSDictionary *receiptDict       = [self dictionaryFromPlistData:transaction.transactionReceipt];
    NSString *transactionPurchaseInfo = [receiptDict objectForKey:@"purchase-info"];
    NSString *decodedPurchaseInfo   = [self decodeBase64:transactionPurchaseInfo length:nil];
    NSDictionary *purchaseInfoDict  = [self dictionaryFromPlistData:[decodedPurchaseInfo dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSString *transactionId         = [purchaseInfoDict objectForKey:@"transaction-id"];
    NSString *purchaseDateString    = [purchaseInfoDict objectForKey:@"purchase-date"];
    NSString *signature             = [receiptDict objectForKey:@"signature"];
    
    // Convert the string into a date
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];
    
    NSDate *purchaseDate = [dateFormat dateFromString:[purchaseDateString stringByReplacingOccurrencesOfString:@"Etc/" withString:@""]];
    
    
    if (![self isTransactionIdUnique:transactionId])
    {
        // We've seen this transaction before.
        // Had [transactionsReceiptStorageDictionary objectForKey:transactionId]
        // Got purchaseInfoDict
        return NO;
    }
    
    // Check the authenticity of the receipt response/signature etc.
    
    BOOL result = checkReceiptSecurity(transactionPurchaseInfo, signature,
                                       (__bridge CFDateRef)(purchaseDate));
    
    if (!result)
    {
        return NO;
    }
    
    // Ensure the transaction itself is legit
    if (![self doTransactionDetailsMatchPurchaseInfo:transaction withPurchaseInfo:purchaseInfoDict])
    {
        return NO;
    }
    
    // Make a note of the fact that we've seen the transaction id already
    [self saveTransactionId:transactionId];
    
    // Save the transaction receipt's purchaseInfo in the transactionsReceiptStorageDictionary.
    [transactionsReceiptStorageDictionary setObject:purchaseInfoDict forKey:transactionId];
    
    return YES;
    }
    
    // Make sure the transaction details actually match the purchase info
    - (BOOL)doTransactionDetailsMatchPurchaseInfo:(SKPaymentTransaction *)transaction withPurchaseInfo:(NSDictionary *)purchaseInfoDict
    
    {
    if (!transaction || !purchaseInfoDict)
    {
        return NO;
    }
    
    int failCount = 0;
    
    if (![transaction.payment.productIdentifier isEqualToString:[purchaseInfoDict objectForKey:@"product-id"]])
    {
    
        failCount++;
    }
    
    if (transaction.payment.quantity != [[purchaseInfoDict objectForKey:@"quantity"] intValue])
    {
        failCount++;
    }
    
    if (![transaction.transactionIdentifier isEqualToString:[purchaseInfoDict objectForKey:@"transaction-id"]])
    {
        failCount++;
    }
    
    // Optionally check the bid and bvrs match this app's current bundle ID and bundle version.
    // Optionally check the requestData.
    // Optionally check the dates.
    
    if (failCount != 0)
    {
        return NO;
    }
    
    // The transaction and its signed content seem ok.
    return YES;
    }
    
    
    
    - (BOOL)isTransactionIdUnique:(NSString *)transactionId
    {
    NSString *transactionDictionary = KNOWN_TRANSACTIONS_KEY;
    // Save the transactionId to the standardUserDefaults so we can check against that later
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults synchronize];
    
    if (![defaults objectForKey:transactionDictionary])
    {
        [defaults setObject:[[NSMutableDictionary alloc] init] forKey:transactionDictionary];
        [defaults synchronize];
    }
    
    if (![[defaults objectForKey:transactionDictionary] objectForKey:transactionId])
    {
        return YES;
    }
    // The transaction already exists in the defaults.
    return NO;
    }
    
    
    - (void)saveTransactionId:(NSString *)transactionId
    {
    // Save the transactionId to the standardUserDefaults so we can check against that later
    // If dictionary exists already then retrieve it and add new transactionID
    // Regardless save transactionID to dictionary which gets saved to NSUserDefaults
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *transactionDictionary = KNOWN_TRANSACTIONS_KEY;
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:
                                       [defaults objectForKey:transactionDictionary]];
    if (!dictionary)
    {
        dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:1], transactionId, nil];
    } else {
        [dictionary setObject:[NSNumber numberWithInt:1] forKey:transactionId];
    }
    [defaults setObject:dictionary forKey:transactionDictionary];
    [defaults synchronize];
    
    }
    
    
    - (BOOL)doesTransactionInfoMatchReceipt:(NSString*) receiptString
    {
    // Convert the responseString into a dictionary and pull out the receipt data.
    NSDictionary *verifiedReceiptDictionary = [self dictionaryFromJSONData:[receiptString dataUsingEncoding:NSUTF8StringEncoding]];
    
    // Check the status of the verifyReceipt call
    id status = [verifiedReceiptDictionary objectForKey:@"status"];
    if (!status)
    {
        return NO;
    }
    int verifyReceiptStatus = [status integerValue];
    // 21006 = This receipt is valid but the subscription has expired.
    if (0 != verifyReceiptStatus && 21006 != verifyReceiptStatus)
    {
        return NO;
    }
    
    // The receipt is valid, so checked the receipt specifics now.
    
    NSDictionary *verifiedReceiptReceiptDictionary  = [verifiedReceiptDictionary objectForKey:@"receipt"];
    NSString *verifiedReceiptUniqueIdentifier       = [verifiedReceiptReceiptDictionary objectForKey:@"unique_identifier"];
    NSString *transactionIdFromVerifiedReceipt      = [verifiedReceiptReceiptDictionary objectForKey:@"transaction_id"];
    
    // Get the transaction's receipt data from the transactionsReceiptStorageDictionary
    NSDictionary *purchaseInfoFromTransaction = [transactionsReceiptStorageDictionary objectForKey:transactionIdFromVerifiedReceipt];
    
    if (!purchaseInfoFromTransaction)
    {
        // We didn't find a receipt for this transaction.
        return NO;
    }
    
    
    // NOTE: Instead of counting errors you could just return early.
    int failCount = 0;
    
    // Verify all the receipt specifics to ensure everything matches up as expected
    if (![[verifiedReceiptReceiptDictionary objectForKey:@"bid"]
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@"bid"]])
    {
        failCount++;
    }
    
    if (![[verifiedReceiptReceiptDictionary objectForKey:@"product_id"]
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@"product-id"]])
    {
        failCount++;
    }
    
    if (![[verifiedReceiptReceiptDictionary objectForKey:@"quantity"]
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@"quantity"]])
    {
        failCount++;
    }
    
    if (![[verifiedReceiptReceiptDictionary objectForKey:@"item_id"]
          isEqualToString:[purchaseInfoFromTransaction objectForKey:@"item-id"]])
    {
        failCount++;
    }
    
    if ([[UIDevice currentDevice] respondsToSelector:NSSelectorFromString(@"identifierForVendor")]) // iOS 6?
    {
    #if IS_IOS6_AWARE
        // iOS 6 (or later)
        NSString *localIdentifier                   = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
        NSString *purchaseInfoUniqueVendorId        = [purchaseInfoFromTransaction objectForKey:@"unique-vendor-identifier"];
        NSString *verifiedReceiptVendorIdentifier   = [verifiedReceiptReceiptDictionary objectForKey:@"unique_vendor_identifier"];
    
    
        if(verifiedReceiptVendorIdentifier)
        {
            if (![purchaseInfoUniqueVendorId isEqualToString:verifiedReceiptVendorIdentifier]
                || ![purchaseInfoUniqueVendorId isEqualToString:localIdentifier])
            {
                // Comment this line out to test in the Simulator.
                failCount++;
            }
        }
    #endif
    } else {
        // Pre iOS 6 
    //        NSString *localIdentifier           = [UIDevice currentDevice].uniqueIdentifier;
    //        NSString *purchaseInfoUniqueId      = [purchaseInfoFromTransaction objectForKey:@"unique-identifier"];
    //        if (![purchaseInfoUniqueId isEqualToString:verifiedReceiptUniqueIdentifier]
    //            || ![purchaseInfoUniqueId isEqualToString:localIdentifier])
    //        {
    //            // Comment this line out to test in the Simulator.
    //            failCount++;
    //        }        
    }
    
    
    // Do addition time checks for the transaction and receipt.
    
    if(failCount != 0)
    {
        return NO;
    }
    
    return YES;
    }
    
    
    #pragma mark NSURLConnectionDelegate (for the verifyReceipt connection)
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    
    NSLog(@"Connection failure: %@", error);
    
    VerifyCompletionHandler completionHandler = _completionHandlers[[NSValue valueWithNonretainedObject:connection]];
    [_completionHandlers removeObjectForKey:[NSValue valueWithNonretainedObject:connection]];
    completionHandler(FALSE);
    
    }
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    BOOL isOk = [self doesTransactionInfoMatchReceipt:responseString];
    
    VerifyCompletionHandler completionHandler = _completionHandlers[[NSValue valueWithNonretainedObject:connection]];
    [_completionHandlers removeObjectForKey:[NSValue valueWithNonretainedObject:connection]];
    if (isOk)
    {
        //Validation suceeded. Unlock content here.
        NSLog(@"Validation successful");
        completionHandler(TRUE);
    
    } else {
        NSLog(@"Validation failed");
        completionHandler(FALSE);
    }
    }
    - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        SecTrustRef trust = [[challenge protectionSpace] serverTrust];
        NSError *error = nil;
        BOOL didUseCredential = NO;
        BOOL isTrusted = [self validateTrust:trust error:&error];
        if (isTrusted)
        {
            NSURLCredential *trust_credential = [NSURLCredential credentialForTrust:trust];
            if (trust_credential)
            {
                [[challenge sender] useCredential:trust_credential forAuthenticationChallenge:challenge];
                didUseCredential = YES;
            }
        }
        if (!didUseCredential)
        {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    } else {
        [[challenge sender] performDefaultHandlingForAuthenticationChallenge:challenge];
    }
    }
    
    // NOTE: These are needed for 4.x (as willSendRequestForAuthenticationChallenge: is not supported)
    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
    {
    return [[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust];
    }
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
        {
        SecTrustRef trust = [[challenge protectionSpace] serverTrust];
        NSError *error = nil;
        BOOL didUseCredential = NO;
        BOOL isTrusted = [self validateTrust:trust error:&error];
        if (isTrusted)
        {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];
            if (credential)
            {
                [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
                didUseCredential = YES;
            }
        }
        if (! didUseCredential) {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    } else {
        [[challenge sender] performDefaultHandlingForAuthenticationChallenge:challenge];
    }
    }
    
    
    #pragma mark
    #pragma mark NSURLConnection - Trust validation
    
    - (BOOL)validateTrust:(SecTrustRef)trust error:(NSError **)error
    {
    
    // Include some Security framework SPIs
    extern CFStringRef kSecTrustInfoExtendedValidationKey;
    extern CFDictionaryRef SecTrustCopyInfo(SecTrustRef trust);
    
    BOOL trusted = NO;
    SecTrustResultType trust_result;
    if ((noErr == SecTrustEvaluate(trust, &trust_result)) && (trust_result == kSecTrustResultUnspecified))
    {
        NSDictionary *trust_info = (__bridge_transfer NSDictionary *)SecTrustCopyInfo(trust);
        id hasEV = [trust_info objectForKey:(__bridge NSString *)kSecTrustInfoExtendedValidationKey];
        trusted =  [hasEV isKindOfClass:[NSValue class]] && [hasEV boolValue];
    }
    
    if (trust)
    {
        if (!trusted && error)
        {
            *error = [NSError errorWithDomain:@"kSecTrustError" code:(NSInteger)trust_result userInfo:nil];
        }
        return trusted;
    }
    return NO;
    }
    #pragma mark
    #pragma mark Base 64 encoding
    
    - (NSString *)encodeBase64:(const uint8_t *)input length:(NSInteger)length
    {
    NSData * data = [NSData dataWithBytes:input length:length];
    return [data base64EncodedString];
    }
    - (NSString *)decodeBase64:(NSString *)input length:(NSInteger *)length
    {
    NSData * data = [NSData dataFromBase64String:input];
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }
    
    char* base64_encode(const void* buf, size_t size) {
    size_t outputLength;
    return NewBase64Encode(buf, size, NO, &outputLength);
    }
    void * base64_decode(const char* s, size_t * data_len)
    {
     return NewBase64Decode(s, strlen(s), data_len);
    }
    @end
    #pragma mark
    #pragma mark Check Receipt signature
    #include <CommonCrypto/CommonDigest.h>
    #include <Security/Security.h>
    #include <AssertMacros.h>
    unsigned int iTS_intermediate_der_len = 1039;
    
    unsigned char iTS_intermediate_der[] = {
    put the hexacode here
    };
    
    BOOL checkReceiptSecurity(NSString *purchase_info_string, NSString *signature_string, CFDateRef purchaseDate)
    {
    BOOL valid = NO;
    SecCertificateRef leaf = NULL, intermediate = NULL;
    SecTrustRef trust = NULL;
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    
    NSData *certificate_data;
    NSArray *anchors;
    
    /*
     Parse inputs:
     purchase_info_string and signature_string are base64 encoded JSON blobs that need to
     be decoded.
     */
    
    require([purchase_info_string canBeConvertedToEncoding:NSASCIIStringEncoding] &&
            [signature_string canBeConvertedToEncoding:NSASCIIStringEncoding], outLabel);
    
    size_t purchase_info_length;
    uint8_t *purchase_info_bytes = base64_decode([purchase_info_string cStringUsingEncoding:NSASCIIStringEncoding],
                                                 &purchase_info_length);
    
    size_t signature_length;
    uint8_t *signature_bytes = base64_decode([signature_string cStringUsingEncoding:NSASCIIStringEncoding],
                                             &signature_length);
    
    require(purchase_info_bytes && signature_bytes, outLabel);
    
    /*
     Binary format looks as follows:
    
     RECEIPTVERSION | SIGNATURE | CERTIFICATE SIZE | CERTIFICATE
     1 byte           128         4 bytes
     big endian
    
     Extract version, signature and certificate(s).
     Check receipt version == 2.
     Sanity check that signature is 128 bytes.
     Sanity check certificate size <= remaining payload data.
     */
    
    #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)
    uint32_t certificate_len;
    
    /*
     Make sure the signature blob is long enough to safely extract the version and
     cert_len fields, then perform a sanity check on the fields.
     */
    require(signature_length > offsetof(struct signature_blob, certificate), outLabel);
    require(signature_blob_ptr->version == 2, outLabel);
    certificate_len = ntohl(signature_blob_ptr->cert_len);
    
    require(signature_length - offsetof(struct signature_blob, certificate) >= certificate_len, outLabel);
    
    /*
     Validate certificate chains back to valid receipt signer; policy approximation for now
     set intermediate as a trust anchor; current intermediate lapses in 2016.
     */
    
    certificate_data = [NSData dataWithBytes:signature_blob_ptr->certificate length:certificate_len];
    require(leaf = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certificate_data), outLabel);
    
    certificate_data = [NSData dataWithBytes:iTS_intermediate_der length:iTS_intermediate_der_len];
    require(intermediate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certificate_data), outLabel);
    
    anchors = [NSArray arrayWithObject:(__bridge id)intermediate];
    require(anchors, outLabel);
    
    require_noerr(SecTrustCreateWithCertificates(leaf, policy, &trust), outLabel);
    require_noerr(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef) anchors), outLabel);
    
    if (purchaseDate)
    {
        require_noerr(SecTrustSetVerifyDate(trust, purchaseDate), outLabel);
    }
    
    SecTrustResultType trust_result;
    require_noerr(SecTrustEvaluate(trust, &trust_result), outLabel);
    require(trust_result == kSecTrustResultUnspecified, outLabel);
    
    require(2 == SecTrustGetCertificateCount(trust), outLabel);
    
    /*
     Chain is valid, use leaf key to verify signature on receipt by
     calculating SHA1(version|purchaseInfo)
     */
    
    CC_SHA1_CTX sha1_ctx;
    uint8_t to_be_verified_data[CC_SHA1_DIGEST_LENGTH];
    
    CC_SHA1_Init(&sha1_ctx);
    CC_SHA1_Update(&sha1_ctx, &signature_blob_ptr->version, sizeof(signature_blob_ptr->version));
    CC_SHA1_Update(&sha1_ctx, purchase_info_bytes, purchase_info_length);
    CC_SHA1_Final(to_be_verified_data, &sha1_ctx);
    
    SecKeyRef receipt_signing_key = SecTrustCopyPublicKey(trust);
    require(receipt_signing_key, outLabel);
    require_noerr(SecKeyRawVerify(receipt_signing_key, kSecPaddingPKCS1SHA1,
                                  to_be_verified_data, sizeof(to_be_verified_data),
                                  signature_blob_ptr->signature, sizeof(signature_blob_ptr->signature)),
                  outLabel);
    
    /*
     Optional:  Verify that the receipt certificate has the 1.2.840.113635.100.6.5.1 Null OID
    
     The signature is a 1024-bit RSA signature.
     */
    
    valid = YES;
    
    outLabel:
    if (leaf) CFRelease(leaf);
    if (intermediate) CFRelease(intermediate);
    if (trust) CFRelease(trust);
    if (policy) CFRelease(policy);
    
    return valid;
    }
    

    【讨论】:

    • SecTrustCopyPublicKey 函数似乎存在内存泄漏-receipt_signing_key 从未 CFRelease'd。我认为在 outLabel 中:它需要一个 if (receipt_signing_key) CFRelease(receipt_signing_key);然后需要在函数的开头声明receipt_signing_key并将其设置为NULL,然后再使用require语句来解决此问题。我知道这最初来自 Apple 的源代码,所以这个泄漏可能在很多地方。
    • 如果我可以投票 100 次,我会的!我不小心删除了我的目标,不得不重新创建它,这让我遇到了 VerificationController 的问题!按照您的建议重新排序就可以了。谢谢!
    猜你喜欢
    • 2011-10-12
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 2013-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多