SORU
13 Kasım 2013, ÇARŞAMBA


YEREL olarak uygulama içi bir makbuz doğrulamak için çözüm ve paket fişleri iOS 7 tam bir

Teori-Uygulama ve/veya paket makbuz doğrulamak olacak dokümanlar ve kod bir sürü okudum.

SSL bilgim, sertifikalar, şifreleme göz önüne alındığında, vb., neredeyse sıfır, okudum, like this promising one, anlamak zor buldum.

Derler açıklamalar eksik, çünkü her kişi için anlamaya nasıl yapacağını, ya da hackerlar olacak kolay bir iş oluşturma kraker app tanıyabilen ve desenleri tespit ve düzeltme uygulaması. TAMAM, bu belli bir noktaya kadar katılıyorum. Bence onlar tamamen açıklamak için nasil bir uyarı "diyerek değiştirmek bu yöntem", "değiştir bu diğer yöntem", "karartan bu değişken", "değişim adı bu ve o", vb.

Orada bazı iyi ruh açıklamak için yeterli olabilirYEREL olarak, paket fişleri nasıl doğrulamak ve ın-app iOS 7 fişleri satın alınbeş yaşındayım (Tamam, 3), yukarıdan aşağıya doğru, net bir şekilde?

!! Teşekkürler!


Bir sürüm uygulamalarınız için çalışan ve endişelerinizi hackerlar bunu nasıl yaptığını göreceksiniz, sadece burada yayınlamadan önce hassas yöntemlerini değiştirmek. Dizeleri karartmak, satır sırasını değiştirmek, döngüler (numaralandırma bloğu ve tersi kullanarak) ve bunun gibi şeyler yapmak şeklini değiştirmek. Belli ki, burada yayınlanan olabilir, bu kodu kullanan her kişi aynı şeyi, kolayca ele geçirilmesini risk değil.

CEVAP
18 Kasım 2013, PAZARTESİ


İşte in-app satın alma kütüphanemdeki bu çözdüm nasıl örneklerde RMStore. Fişin doğrulama içeren bir işlem doğrulamak için nasıl izah edeceğim.

Bir bakışta

Fiş almak ve işlemleri kontrol. Eğer başarısız olursa, fişi yenileyin ve tekrar deneyin. Bu doğrulama işlemi makbuz asenkron serinletici olarak zaman uyumsuz yapar.

RMStoreAppReceiptVerificator:

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
const BOOL verified = [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:nil]; // failureBlock is nil intentionally. See below.
if (verified) return;

// Apple recommends to refresh the receipt if validation fails on iOS
[[RMStore defaultStore] refreshReceiptOnSuccess:^{
    RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
    [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:failureBlock];
} failure:^(NSError *error) {
    [self failWithBlock:failureBlock error:error];
}];

Girişi verileri alıyorum

Makbuz [[NSBundle mainBundle] appStoreReceiptURL] ve aslında PCKS7 bir kaptır. OpenSSL bu kabı açmak için kullandım kriptografi çok kötüyüm. Diğerleri görünüşte system frameworks ile tamamen yaptı.

Projeniz için OpenSSL ekleme önemsiz değildir. RMStore wiki yardımcı olmalıdır.

Eğer OpenSSL PKCS7 konteyner açmak için kullanmayı tercih ederseniz, sizin kod şöyle. RMAppReceipt:

  (NSData*)dataFromPKCS7Path:(NSString*)path
{
    const char *cpath = [[path stringByStandardizingPath] fileSystemRepresentation];
    FILE *fp = fopen(cpath, "rb");
    if (!fp) return nil;

    PKCS7 *p7 = d2i_PKCS7_fp(fp, NULL);
    fclose(fp);

    if (!p7) return nil;

    NSData *data;
    NSURL *certificateURL = [[NSBundle mainBundle] URLForResource:@"AppleIncRootCertificate" withExtension:@"cer"];
    NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL];
    if ([self verifyPKCS7:p7 withCertificateData:certificateData])
    {
        struct pkcs7_st *contents = p7->d.sign->contents;
        if (PKCS7_type_is_data(contents))
        {
            ASN1_OCTET_STRING *octets = contents->d.data;
            data = [NSData dataWithBytes:octets->data length:octets->length];
        }
    }
    PKCS7_free(p7);
    return data;
}

Doğrulama ayrıntıları sonra konuşuruz.

Makbuz alanları alıyorum

Makbuz ASN1 biçiminde ifade edilir. Genel bilgiler, doğrulama amacıyla bazı alanlar (buna daha sonra geleceğiz) ve app içinde satın alma, her ilgili belirli bilgiler içerir.

Yine OpenSSL ASN1 okumaya gelince kurtarmaya gelir. RMAppReceipt, birkaç yardımcı yöntemleri kullanarak:

NSMutableArray *purchases = [NSMutableArray array];
[RMAppReceipt enumerateASN1Attributes:asn1Data.bytes length:asn1Data.length usingBlock:^(NSData *data, int type) {
    const uint8_t *s = data.bytes;
    const NSUInteger length = data.length;
    switch (type)
    {
        case RMAppReceiptASN1TypeBundleIdentifier:
            _bundleIdentifierData = data;
            _bundleIdentifier = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeAppVersion:
            _appVersion = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeOpaqueValue:
            _opaqueValue = data;
            break;
        case RMAppReceiptASN1TypeHash:
            _hash = data;
            break;
        case RMAppReceiptASN1TypeInAppPurchaseReceipt:
        {
            RMAppReceiptIAP *purchase = [[RMAppReceiptIAP alloc] initWithASN1Data:data];
            [purchases addObject:purchase];
            break;
        }
        case RMAppReceiptASN1TypeOriginalAppVersion:
            _originalAppVersion = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeExpirationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&s, length);
            _expirationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
    }
}];
_inAppPurchases = purchases;

Alma-app alımları

Uygulama içi her alım da ASN1. Ayrıştırma genel fiş bilgileri ayrıştırma daha çok benzer.

RMAppReceipt aynı yardımcı yöntemleri kullanarak:

[RMAppReceipt enumerateASN1Attributes:asn1Data.bytes length:asn1Data.length usingBlock:^(NSData *data, int type) {
    const uint8_t *p = data.bytes;
    const NSUInteger length = data.length;
    switch (type)
    {
        case RMAppReceiptASN1TypeQuantity:
            _quantity = RMASN1ReadInteger(&p, length);
            break;
        case RMAppReceiptASN1TypeProductIdentifier:
            _productIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypeTransactionIdentifier:
            _transactionIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypePurchaseDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _purchaseDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeOriginalTransactionIdentifier:
            _originalTransactionIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypeOriginalPurchaseDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _originalPurchaseDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeSubscriptionExpirationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _subscriptionExpirationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeWebOrderLineItemID:
            _webOrderLineItemID = RMASN1ReadInteger(&p, length);
            break;
        case RMAppReceiptASN1TypeCancellationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _cancellationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
    }
}]; 

-App alımları, sarf malzemeleri gibi belirli olmayan yenilenebilir abonelikleri, sadece bir kez fiş görünür unutulmamalıdır. Satın aldıktan sonra bu hakkı (yine RMStore bu konuda size yardımcı olur) doğrulamak gerekir.

Bir bakışta doğrulama

Şimdi makbuz ve app tüm alımlarını tüm alanları var. İlk biz fişi kendisini doğrulamak, ve sonra biz eğer fişi işlem ürün varsa sadece kontrol edin.

Aşağıda baştan dediğimiz yöntemi. RMStoreAppReceiptVerificator:

- (BOOL)verifyTransaction:(SKPaymentTransaction*)transaction
                inReceipt:(RMAppReceipt*)receipt
                           success:(void (^)())successBlock
                           failure:(void (^)(NSError *error))failureBlock
{
    const BOOL receiptVerified = [self verifyAppReceipt:receipt];
    if (!receiptVerified)
    {
        [self failWithBlock:failureBlock message:NSLocalizedString(@"The app receipt failed verification", @"")];
        return NO;
    }
    SKPayment *payment = transaction.payment;
    const BOOL transactionVerified = [receipt containsInAppPurchaseOfProductIdentifier:payment.productIdentifier];
    if (!transactionVerified)
    {
        [self failWithBlock:failureBlock message:NSLocalizedString(@"The app receipt doest not contain the given product", @"")];
        return NO;
    }
    if (successBlock)
    {
        successBlock();
    }
    return YES;
}

Makbuz doğrulanıyor

Kendini aşağı kaynar makbuz doğrulama:

  1. Makbuz olduğunu kontrol ediyor geçerli PKCS7 ve ASN1. Bu örtülü olarak zaten yaptık.
  2. Fiş Apple tarafından imzalanmış doğrulanıyor. Bu makbuz ayrıştırma önce yapıldı ve aşağıda ayrıntılı olacak.
  3. Paket tanımlayıcısı faturası dahil kontrol paket tanımlayıcısı karşılık gelir. App bundle değiştirmek ve başka bir girişi kullanmak çok zor görünmüyor gibi paket tanımlayıcı koda gerekir.
  4. Uygulama Sürümü fişi dahil app sürümü için karşılık gelen kontrol tanımlayıcısı. Uygulama sürümü, aynı yukarıda belirtilen nedenlerden koda gerekir.
  5. Emin faturası mevcut cihaz için uygun yapmak için fiş karma kontrol edin.

RMStoreAppReceiptVerificator: yüksek düzey, kod 5 adımları

- (BOOL)verifyAppReceipt:(RMAppReceipt*)receipt
{
    // Steps 1 & 2 were done while parsing the receipt
    if (!receipt) return NO;   

    // Step 3
    if (![receipt.bundleIdentifier isEqualToString:self.bundleIdentifier]) return NO;

    // Step 4        
    if (![receipt.appVersion isEqualToString:self.bundleVersion]) return NO;

    // Step 5        
    if (![receipt verifyReceiptHash]) return NO;

    return YES;
}

Hadi detayına adım 2 ve 5.

Makbuz imza doğrulanıyor

Veri çıkardığımızda geri alınmasından imza doğrulama üzerinden baktı. Fiş Apple Inc ile imzalandı. Apple Root Certificate Authority indirilebilir Belgesi, kök. Aşağıdaki kodu PKCS7 konteyner ve eğer bunlar aynı veri ve çek gibi sertifika: kök alır

  (BOOL)verifyPKCS7:(PKCS7*)container withCertificateData:(NSData*)certificateData
{ // Based on: https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW17
    static int verified = 1;
    int result = 0;
    OpenSSL_add_all_digests(); // Required for PKCS7_verify to work
    X509_STORE *store = X509_STORE_new();
    if (store)
    {
        const uint8_t *certificateBytes = (uint8_t *)(certificateData.bytes);
        X509 *certificate = d2i_X509(NULL, &certificateBytes, (long)certificateData.length);
        if (certificate)
        {
            X509_STORE_add_cert(store, certificate);

            BIO *payload = BIO_new(BIO_s_mem());
            result = PKCS7_verify(container, NULL, store, NULL, payload, 0);
            BIO_free(payload);

            X509_free(certificate);
        }
    }
    X509_STORE_free(store);
    EVP_cleanup(); // Balances OpenSSL_add_all_digests (), perhttp://www.openssl.org/docs/crypto/OpenSSL_add_all_algorithms.html

    return result == verified;
}

Bu makbuz ayrıştırılır önce baştan yapıldı.

Makbuz doğrulama karma

Karma fişi dahil aygıt kimliğini bir SHA1, bazı opak değer girişi dahil ve paket kimliğidir.

Bu iOS fiş karma doğrulayın. RMAppReceipt:

- (BOOL)verifyReceiptHash
{
    // TODO: Getting the uuid in Mac is different. See: https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW5
    NSUUID *uuid = [[UIDevice currentDevice] identifierForVendor];
    unsigned char uuidBytes[16];
    [uuid getUUIDBytes:uuidBytes];

    // Order taken from: https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW5
    NSMutableData *data = [NSMutableData data];
    [data appendBytes:uuidBytes length:sizeof(uuidBytes)];
    [data appendData:self.opaqueValue];
    [data appendData:self.bundleIdentifierData];

    NSMutableData *expectedHash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
    SHA1(data.bytes, data.length, expectedHash.mutableBytes);

    return [expectedHash isEqualToData:self.hash];
}

Ve bunun gibi bir şey. Bu yazı daha sonra geri olabilir bu yüzden burada ya da orada bir şey eksik olabilir. Her durumda, daha fazla bilgi için tam kod tarama tavsiye ederim.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Benjamin Heckendorn

    Benjamin Hec

    4 Mayıs 2008
  • Microsoft Research

    Microsoft Re

    24 EKİM 2008
  • William Sledd

    William Sled

    24 EYLÜL 2006