दिलचस्प पोस्ट
SQL या TSQL ट्यूरिंग पूर्ण है? पीएचपी में बड़ी एक्सएमएल की प्रक्रिया का सबसे अच्छा तरीका एंड्रॉइड स्क्रीन पर छवियां खींचें और ड्रॉप करें? आधुनिक 1000000007 प्रश्नों में सहायता की आवश्यकता है UIPanGestureRecognizer – केवल ऊर्ध्वाधर या क्षैतिज रूबी में सिंगलटन क्लास बिल्कुल क्या है? मैं एक गिट धक्का कैसे ठीक से बाध्य कर सकता हूं? इसे बनाया गया है के बाद एक Sqlite3 तालिका में DELETE CASCADE व्यवहार जोड़ें मेरे फ़ंक्शन को सरणी स्विफ्ट की औसत गणना करना .NET के साथ CSV पार्सिंग विकल्प मावेन को इतना बुरा प्रतिनिधि क्यों है? कस्टम नियंत्रण में अवांछित गुणों को छिपाते हुए KeyListener JPanel के लिए काम नहीं कर रहा है क्या जावा में स्थिर तरीके से विरासत मिली है? Data.frame में मौजूद मानों के लिए पंक्तियाँ जोड़ने का सबसे तेज़ तरीका?

आईओएस 7 पर एक इन-ऐप रसीदों और बंडल प्राप्तियों को स्थानीय रूप से मान्य करने का पूरा समाधान

मैंने बहुत सारे डॉक्स और कोड पढ़ा है जो सिद्धांत में इन-ऐप और / या बंडल रसीद को मान्य करेगा।

यह देखते हुए कि एसएसएल, प्रमाण पत्र, एन्क्रिप्शन इत्यादि का मेरा ज्ञान लगभग शून्य है, मैंने जो भी स्पष्टीकरण पढ़ा है, इस आशाजनक रूप से मुझे समझना मुश्किल हो गया है।

वे कहते हैं कि स्पष्टीकरण अधूरे हैं क्योंकि हर व्यक्ति को यह करना है कि यह कैसे करना है, या हैकर के पास एक क्रैकर ऐप बनना आसान काम होगा जो पहचान और पहचान कर सकते हैं और आवेदन को पैच कर सकते हैं। ठीक है, मैं इसे एक निश्चित बिंदु तक सहमत हूं मुझे लगता है कि वे पूरी तरह से इसे कैसे समझा सकते हैं और "इस पद्धति को संशोधित करें", "इस अन्य विधि को संशोधित करें", "इस वैरिएबल को अस्पष्ट करें", "इस और उसका नाम बदलें" आदि चेतावनी देते हैं।

क्या कुछ अच्छी आत्मा बाहर हो सकती है, यह समझाने के लिए पर्याप्त है कि कैसे आईओएस 7 पर स्थानीय रूप से मान्य, बंडल प्राप्तियां और इन-ऐप खरीद प्राप्ति के बारे में बताएं कि मैं पांच साल का हूँ (ठीक है, इसे 3 बनायें ), ऊपर से नीचे तक, स्पष्ट रूप से?

धन्यवाद!!!


यदि आपके पास अपने एप पर काम कर रहे एक संस्करण है और आपकी चिंताओं यह है कि हैकर्स यह देखेंगे कि आपने यह कैसे किया, बस यहाँ प्रकाशित करने से पहले अपने संवेदनशील तरीकों को बदल दें। आच्छादित स्ट्रिंग्स, लाइनों के क्रम में परिवर्तन करें, जिस तरह से आप लूप करते हैं (इन्हें ब्लॉक करने के लिए उपयोग करने के लिए और उपाध्यक्ष विपरीत) और उस जैसी चीजों को बदल दें। जाहिर है, हर व्यक्ति जो कोड का उपयोग करता है जो यहां पोस्ट किया जा सकता है, उसे एक ही काम करना पड़ता है, आसानी से हैक होने के जोखिम के लिए नहीं।

Solutions Collecting From Web of "आईओएस 7 पर एक इन-ऐप रसीदों और बंडल प्राप्तियों को स्थानीय रूप से मान्य करने का पूरा समाधान"

यहां पर मैंने यह कैसे अपने इन-ऐप खरीद लाइब्रेरी आरएमएसटोर में हल किया है इस बारे में जानकारी दी है । मैं यह बताएगा कि लेनदेन कैसे सत्यापित किया जाए, जिसमें पूरी रसीद की पुष्टि होनी चाहिए।

एक नजर में

रसीद प्राप्त करें और लेनदेन की पुष्टि करें यदि यह विफल रहता है, तो रसीद ताज़ा करें और फिर से प्रयास करें। यह सत्यापन प्रक्रिया को अतुल्यकालिक के रूप में ताज़ा करता है जब रसीद अतुल्यकालिक होती है।

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]; }]; 

रसीद डेटा प्राप्त करना

रसीद [[NSBundle mainBundle] appStoreReceiptURL] और वास्तव में एक पीसीके 7 कंटेनर है मैं क्रिप्टोग्राफ़ी में चूसना है इसलिए मैंने ओपनएसएसएल का इस्तेमाल इस कंटेनर को खोलने के लिए किया था। दूसरों ने जाहिरा तौर पर सिस्टम फ़्रेमवर्क के साथ यह पूरी तरह से किया है ।

अपने प्रोजेक्ट में ओपनएसएसएल जोड़ना तुच्छ नहीं है आरएमएसटोर विकी को मदद करनी चाहिए

यदि आप पीकेसीएस 7 कंटेनर को खोलने के लिए ओपनएसएसएल का उपयोग करने का विकल्प चुनते हैं, तो आपका कोड इस तरह दिख सकता है। 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; } 

हम सत्यापन के विवरण बाद में मिलेंगे।

रसीद फ़ील्ड प्राप्त करना

रसीद ASN1 प्रारूप में व्यक्त की गई है। इसमें सामान्य जानकारी है, सत्यापन के प्रयोजनों के लिए कुछ फ़ील्ड्स (हम उसके बाद बाद में आएँगे) और प्रत्येक लागू इन-ऐप खरीदारी की विशिष्ट जानकारी।

फिर, जब एएसएन 1 पढ़ने की बात आती है तो ओपनएसएसएल बचाव में आता है। कुछ सहायक विधियों का उपयोग करके, RMAppReceipt से:

 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; 

इन-ऐप खरीदारी प्राप्त करना

प्रत्येक इन-ऐप खरीदारी भी ASN1 में है पार्सिंग यह सामान्य रसीद जानकारी को पार्स करने के समान है।

RMAppReceipt से , उसी सहायक विधियों का उपयोग कर:

 [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; } } }]; 

यह ध्यान दिया जाना चाहिए कि कुछ इन-ऐप खरीद, जैसे उपभोग्य और गैर-नवीनीकरण सदस्यता, प्राप्त होने पर केवल एक बार दिखाई देगी। आपको खरीद के बाद इन अधिकारों की पुष्टि करनी चाहिए (फिर से, आरएमएसटोर आपको इसके साथ मदद करता है)

एक नज़र में सत्यापन

अब हमें रसीद और उसके सभी इन-ऐप खरीदारी से सभी फ़ील्ड मिल चुके हैं। सबसे पहले हम रसीद की पुष्टि करते हैं, और फिर हम केवल जांच करते हैं कि रसीद में लेनदेन के उत्पाद शामिल हैं या नहीं।

नीचे वह विधि है जिसे हमने शुरुआत में वापस बुलाया था। 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; } 

रसीद की जांच

रसीद की पुष्टि करने के लिए नीचे फोड़े होते हैं:

  1. यह जांचते हुए कि रसीद वैध PKCS7 और ASN1 है। हमने यह पहले से ही पहले से ही किया है
  2. यह सत्यापित करना कि रसीद एप्पल द्वारा हस्ताक्षरित है यह रसीद पार्स करने से पहले किया गया था और नीचे विस्तृत किया जाएगा।
  3. यह जांचते हुए कि बंडल पहचानकर्ता रसीद में शामिल आपके बंडल पहचानकर्ता से मेल खाती है। आपको अपने बंडल पहचानकर्ता को हार्डकोड करना चाहिए, क्योंकि यह आपके ऐप बंडल को संशोधित करना और कुछ अन्य रसीद का उपयोग करना मुश्किल नहीं लगता है।
  4. जांचते हुए कि एप संस्करण रसीद में शामिल आपके एप संस्करण पहचानकर्ता से संबंधित है। उपरोक्त बताए गए उसी कारणों के लिए आपको एप संस्करण को हार्डकोड करना चाहिए।
  5. रसीद हैश की जाँच करें कि यह सुनिश्चित करने के लिए कि रसीद वर्तमान डिवाइस के अनुरूप है।

RMStoreAppReceiptVerificator से उच्च-स्तर पर कोड में 5 कदम:

 - (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; } 

चलो चरण 2 और 5 में ड्रिल-डाउन।

रसीद हस्ताक्षर की जांच

वापस जब हमने डेटा को निकाला, तो हमने रसीद हस्ताक्षर सत्यापन पर गौर किया। रसीद को एप्पल इंक। रूट सर्टिफिकेट के साथ हस्ताक्षरित किया गया है, जिसे एप्पल रूट प्रमाणपत्र प्राधिकरण से डाउनलोड किया जा सकता है। निम्न कोड PKCS7 कंटेनर और रूट प्रमाण पत्र को डेटा के रूप में लेता है और जांचता है कि वे क्या मेल खाते हैं:

 + (BOOL)verifyPKCS7:(PKCS7*)container withCertificateData:(NSData*)certificateData { // Based on: https://developer.apple.com/library/content/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 (), per http://www.openssl.org/docs/crypto/OpenSSL_add_all_algorithms.html return result == verified; } 

रसीद पार्स होने से पहले, यह शुरुआत में वापस किया गया था।

रसीद हैश की पुष्टि करना

रसीद में शामिल हैश डिवाइस आईडी का एक SHA1 है, रसीद में कुछ अपारदर्शी मूल्य और बंडल आईडी शामिल है

आईओएस पर रसीद हैश की पुष्टि कैसे करें। RMAppReceipt से :

 - (BOOL)verifyReceiptHash { // TODO: Getting the uuid in Mac is different. See: https://developer.apple.com/library/content/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/content/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]; } 

और यह इसका सारांश है। मैं यहाँ या वहां कुछ याद कर सकता हूं, इसलिए मैं बाद में इस पोस्ट पर वापस आ सकता हूं। किसी भी स्थिति में, मैं अधिक विवरणों के लिए पूर्ण कोड ब्राउज़ करने का सुझाव देता हूं।

मुझे कोई आश्चर्य नहीं है कि यहां रिसीगेंस का उल्लेख किया गया है। यह एक ऐसा उपकरण है जो स्वचालित रूप से ऑब्फ़सकेटेड रसीद सत्यापन कोड जनरेट करता है, हर बार एक अलग से एक; यह जीयूआई और कमांड लाइन ऑपरेशन दोनों का समर्थन करता है। अत्यधिक सिफारिशित।

(रिसीजेन से संबद्ध नहीं, केवल एक खुश उपयोगकर्ता।)

मैं इस तरह Rakefile का उपयोग अपने आप को रिसीन करने के लिए स्वचालित रूप से दोबारा शुरू करने के लिए करता हूं (क्योंकि प्रत्येक संस्करण में परिवर्तन की आवश्यकता है) जब मैं rake receigen टाइप करता rake receigen :

 desc "Regenerate App Store Receipt validation code using Receigen app (which must be already installed)" task :receigen do # TODO: modify these to match your app bundle_id = 'com.example.YourBundleIdentifierHere' output_file = File.join(__dir__, 'SomeDir/ReceiptValidation.h') version = PList.get(File.join(__dir__, 'YourProjectFolder/Info.plist'), 'CFBundleVersion') command = %Q</Applications/Receigen.app/Contents/MacOS/Receigen --identifier #{bundle_id} --version #{version} --os ios --prefix ReceiptValidation --success callblock --failure callblock> puts "#{command} > #{output_file}" data = `#{command}` File.open(output_file, 'w') { |f| f.write(data) } end module PList def self.get file_name, key if File.read(file_name) =~ %r!<key>#{Regexp.escape(key)}</key>\s*<string>(.*?)</string>! $1.strip else nil end end end 

नमस्ते यह इन-ऐप-क्रय रसीद के सत्यापन के लिए स्विफ्ट 3 संस्करण है …

अपने AppDelegate से प्राप्त कॉल प्राप्त करने की receiptValidation() फ़ंक्शन या उस सभी से आप कहां से चाहते हैं

  func receiptValidation() { if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) { do { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) let receiptString = receiptData.base64EncodedString(options: []) let dict = ["receipt-data" : receiptString, "password" : "**************************"] as [String : Any] do { let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) //This Url for original Account //let url : String = "https://buy.itunes.apple.com/verifyReceipt" //This Url for Sandbox Testing Account let url : String = "https://sandbox.itunes.apple.com/verifyReceipt" if let sandboxURL = Foundation.URL(string:url) { var request = URLRequest(url: sandboxURL) request.httpMethod = "POST" request.httpBody = jsonData let session = URLSession(configuration: URLSessionConfiguration.default) let task = session.dataTask(with: request) { data, response, error in if let receivedData = data, let httpResponse = response as? HTTPURLResponse, error == nil, httpResponse.statusCode == 200 { do { if let jsonResponse = try JSONSerialization.jsonObject(with: receivedData, options: JSONSerialization.ReadingOptions.mutableContainers) as? Dictionary<String, AnyObject> { if let expirationDate: NSDate = self.expirationDateFromResponse(jsonResponse: jsonResponse as NSDictionary) { let currentDate = self.getCurrentLocalDateApp() if currentDate > expirationDate as Date { self.downgrade("1") }else{ } } } else { } } catch { } }else { print("Error=\(String(describing: error))") } } task.resume() } else { } } catch { } } catch { } } } 

अब हमारे पास एक अन्य कार्य रसीद की expirationDateFromResponse() तिथि से दिनांक प्राप्त कर रहा है expirationDateFromResponse() । यह फ़ंक्शन एक ही नियंत्रक या AppDelegate

 func expirationDateFromResponse(jsonResponse: NSDictionary) -> NSDate? { if let receiptInfo: NSArray = jsonResponse["latest_receipt_info"] as? NSArray { let lastReceipt = receiptInfo.lastObject as! NSDictionary let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV" let expirationDate: NSDate = formatter.date(from: lastReceipt["expires_date"] as! String) as NSDate! formatter.dateStyle = .medium let stringOutput = formatter.string(from: expirationDate as Date) formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let date = formatter.string(from: expirationDate as Date) print("Date=\(date)") self.iosNextBillingDateEntry(date) UserDefaults.standard.set(stringOutput, forKey: "PLAN_EXP_DATE") return expirationDate } else { return nil } } 

अब स्थानीय समय के लिए यदि आपके लिए जरूरी है तो हमारे पास एक और फ़ंक्शन है

 func getCurrentLocalDateApp()-> Date { var now = Date() var nowComponents = DateComponents() let calendar = Calendar.current nowComponents.year = (Calendar.current as NSCalendar).component(NSCalendar.Unit.year, from: now) nowComponents.month = (Calendar.current as NSCalendar).component(NSCalendar.Unit.month, from: now) nowComponents.day = (Calendar.current as NSCalendar).component(NSCalendar.Unit.day, from: now) nowComponents.hour = (Calendar.current as NSCalendar).component(NSCalendar.Unit.hour, from: now) nowComponents.minute = (Calendar.current as NSCalendar).component(NSCalendar.Unit.minute, from: now) nowComponents.second = (Calendar.current as NSCalendar).component(NSCalendar.Unit.second, from: now) nowComponents.timeZone = TimeZone(abbreviation: "VV") now = calendar.date(from: nowComponents)! return now } 

पासवर्ड जो आप सेब स्टोर से मिलेगा https://developer.apple.com इस लिंक को खोलें पर क्लिक करें

  • Account tab
  • Do Sign in
  • Open iTune Connect
  • Open My App
  • Open Feature Tab
  • Open In App Purchase
  • Click at the right side on 'View Shared Secret'
  • At the bottom you will get a secrete key

वह कुंजी कॉपी करें और पासवर्ड फ़ील्ड में पेस्ट करें।

उम्मीद है कि यह प्रत्येक के लिए मदद करेगा जो कि तेज संस्करण में चाहता है।