Updated for Swift 3 (Feb 9, 2017)
Apple has given us a way to send a device’s receipt to their server to verify that it’s authentic. But if you want to prepare for those pesky hackers this is not enough, simply because you can’t control the connection between each person’s device and Apple’s server. The old solution for this was to send the receipt to your own server which you can keep secure, and then send the data to Apple and return the response back to the device. But no one wants to do all that so lets take a look at validating receipts locally on devices.
Apple has very high level documentation on this but it does not go anywhere close to what we need: https://developer.apple.com/library/mac/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html
A few articles and sources that I used for references can be found here, I would suggest reading up on each one if you are new to the subject.
Apple also has various WWDC videos on this subject that are worth watching as well.
If you don’t know already, Apple explains that you need OpenSSL in order to do this locally on a device, but iOS does not come with OpenSSL naturally. They do advise though that when including OpenSSL you need to add it as a static framework so it cannot be swapped out beneath you with a different version of OpenSSL that tells your app that everything is OK.
- People have already published various scripts for creating a static framework from the latest version of OpenSSL and the latest version of iOS on your machine. It will also compile all the frameworks (i386, x86_64, armv7, armv7s, armv64), One example https://github.com/x2on/OpenSSL-for-iPhone
- With the addition of Bitcode this past year and everyone’s need to have an app with Bitcode, make sure you’ve selected a script or an OpenSSL framework that supports Bitcode, else you will have to disable it for your app.
- You should end up with 2 files, libcrypto.a and libssl.a along with a folder containing all the header files.
- After adding these files to your project and you can build with no errors, you need to add the imports to your Bridging-Header.h file.
Important Notes about OpenSSL:
Doing all of this with Swift 1.2 and Xcode 6.4, there were a few things I ran into. In the header file of rsa.h, there was an error with the line
int (*rsa_mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
Since I’m not using RSA for this I simply commented this line out and everything compiled for me.
The second issue is discussed here http://swiftrien.blogspot.com/2015/05/osx-receipt-validation-in-swift-part-4.html, without these additions, I could not convert the Objective-C code into Swift correctly.
What I did, in the header file pkcs7.h after the declaration of the struct named PKCS7 on line 217, I added the following lines from provided in the above link.
Apple Root CA
One of the things mentioned through out Apple’s documentation and videos is validating the receipt with their Root CA. You can find it here http://www.apple.com/certificateauthority/ its the first one at the top left called Apple Inc. Root Certificate. Download this and drop it into your project.
I’m not going to cover ever aspect of this because there is already great information already out there, i.e. http://www.objc.io/issues/17-security/receipt-validation/. One thing I will note is the payload, or the actual part of the receipt that has the data we want about the in app purchases. Through the madness of decoding the ASN.1 payload, we are looking for different values that relate to different fields in the receipt. If you look at other source code for this you will most likely see a long switch statement somewhere checking for these. You can go here to learn what each field type is and what the field value matches to.
If you take a look at the link above, you might notice a section towards the bottom called In-App Purchase Receipt Fields, and each item’s field type has a value starting at 1700 which is much higher than the previous ones. If you are familiar with the receipt’s JSON, you’ll remember that each in app purchase has its own set of values, which is what these are. The field type 17, which is the In-App Purchase Receipt, has a field value of SET, which contains the in-app purchase receipt attributes, this set contains all the values that start with 1700. All of this is important to remember if you want to pull the product Ids from the receipt, which you do! Note: If the only validation you perform is sending the receipt to Apple and receive the nicely structured JSON of the receipt back, you don’t have to do any of this, but this allows you to validate your receipts offline and at anytime, and provides better security.
Time to get Swifty
There are 3rd party libraries out there on Github you can use for all this validation if you want, but as of this writing, they are all in Objective-C. Which might be fine for you, but maybe you want to do all this yourself and in Swift. Note: The general consensus with this is everyone should not use the exact same code for if a hacker learns how to bypass one app, then he can do the same to many more if everyone’s code is the exact same. So I will leave it up to you to structure it how you wish and name your variables to your own.
Now that the receipt has been verified, its time to parse it into a readable format. This is the most lengthly part.
Here is the method I used above for pulling out the product Ids from the receipt. You can easily modify this to do read in more fields if you desire.
If you are having trouble with getting figuring out which type belongs to what, like the V_ASN1_OCTET_STRING, if you print out the value of the variable ‘type’ you should get an integer. You can then go here http://www.obj-sys.com/asn1tutorial/node10.html to determine what data type that value belongs to.