Alexis Taugeron

App Store Receipt Validation on iOS 7

In October 2010, Apple introduced the Mac App Store, and with it a unified receipt stored in the application bundle. Three years later, iOS gets the same treatment with the release of iOS 7.

Unfortunately, validating and parsing the receipt on the device in neither easy, nor well documented. As I discussed this subject on the Apple developer forums, I saw many developers getting stuck at various stages of the process, and some throwing the towel in.

This article contains a few gotchas I had while implementing receipt validation in Tap Tap Chinese. I hope it will save some time (and hair) to my fellow developers.

Where is my receipt?

I used NSBundle’s appStoreReceiptURL to locate my receipt, but there is no file at this URL!

This is normal in a development environment. In order to get a valid receipt from the App Store, you need to start a SKReceiptRefreshRequest and wait for its delegate’s requestDidFinish: method to be called.

When you start the refresh request, iOS will prompt you to enter your App Store credentials. You need to enter a test user’s credentials, otherwise you will get an error. Additionally, this test user must have made a test purchase before.

This can only be done on a device, as StoreKit is not available in the iOS simulator.

Here’s for the lazy ones

Why doesn’t Apple just provide a method to validate and parse the receipt in one line?

The reasoning behind this is that if Apple did, everybody would use the exact same code to validate the receipt, and it would make it easier for pirates to crack your applications.

Instead, they chose to use standard cryptography and encoding techniques (PKCS7 and ASN1) and give hints for implementing the validation and parsing.

Isn’t there some off-the-shelf code I can use?

Yes, there is.

Remember that it is safer to use this code as a reference and write your own implementation, rather than using it directly.

Implementation tips & tricks

I decided to implement my own validation code using OpenSSL and followed Apple’s Receipt Validation Programming Guide, but they don’t explain how to load the receipt data into a PKCS7 variable!

Loading the receipt with OpenSSL
1
2
3
4
5
6
#import <openssl/pkcs7.h>

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
const uint8_t *receiptBytes = (uint8_t *)(receiptData.bytes);
PKCS7 *receipt = d2i_PKCS7(NULL, &receiptBytes, (long)receiptData.length);

Neither do they explain how to load the Apple root certificate! Where do I find it anyway?

You can download the Apple root certificate here. You need to use the first one, called Apple Inc. Root Certificate. Don’t forget to add it to your build target in Xcode.

Loading the Apple root certificate with OpenSSL
1
2
3
4
5
6
#import <openssl/x509.h>

NSString *certPath = [[NSBundle mainBundle] pathForResource:@"AppleIncRootCertificate" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:certPath];
const uint8_t *certBytes = (uint8_t *)(certData.bytes);
X509 *appleRootCertificate = d2i_X509(NULL, &certBytes, (long)certData.length);

I did everything right, but PKCS7_verify keeps failing!

Before you call PKCS7_verify to verify your receipt signature, you need to initialize OpenSSL by calling:

Initializing OpenSSL
1
OpenSSL_add_all_digests();

If you don’t, PKCS7_verify will never return 1 and you’ll spend hours wondering what the hell is wrong with your code.