I am the author of Implementing In-app Purchases on iOS and eight other courses on Pluralsight.
Deepen your understanding by watching the course!
Extracting a PKCS7 Container for Receipt Validation with Swift
So you’ve prepared to test receipt validation by setting up your app in iTunes Connect.
You’ve brought in a cryptography library like OpenSSL to be able to work with the PKCS #7 container that acts as the “envelope” for the receipt. Perhaps you’ve even done it the “easy way” with CocoaPods.
You’ve located and loaded the receipt for validation.
Now you’re ready to extract the PKCS #7 container and work with it.
The aim of this guide is to get you started using the OpenSSL library in your Swift code by employing it to extract the receipt contents from its PKCS #7 container.
Just want the code? Here you go!
Recap from the previous guide
In Loading a Receipt for Validation with Swift, I began the process of breaking out the various steps of the receipt validation process into separate single-responsibility structs with clearly named functions to help clarify what each piece of code is doing.
Recall that I’ve created a main Type called ReceiptValidator
, with references to several smaller single-responsibility Types that it uses to accomplish the overall validation process. So accordingly, as of my last post in the series, I’ve created a ReceiptLoader
that finds the receipt on the file system and loads it into memory.
If a validation step ever fails along the way, I’ve decided to take advantage of Swift’s error throwing features to clearly describe what failed. So far, there’s only one case:
|
|
I’ll expand this enum Type to cover more failure conditions in this guide.
ReceiptExtractor struct outline
The OpenSSL library comes to us in the form of a C static library. It’s not a beautiful API to say the least. The names of the Types and functions are really cryptic at times, so I’ve decided it’s best for my own memory to wrap each step in small function routines that are named for what they do.
So supposing you’ve located and loaded the receipt data, or used Store Kit to request a receipt from Apple… Take a look at this new ReceiptExtractor
skeleton of a struct to get an idea of what’s going to be required to extract the PKCS7 container for the receipt:
|
|
New ReceiptValidationError cases
When extracting the receipt information from the PKCS7 container, there are going to be things that would cause overall validation to fail. For example, if the receipt Data
instance ends up being empty, that’s a validation failure. The PKCS7 container needs to have information inside of it for validation to pass (obviously).
So in this guide, I’ll expand the ReceiptValidationError
enum to have the following cases:
|
|
Preparation step: PKCS7 union accessors
Before attempting to work with OpenSSL’s PKCS7 functions, you’ve got to do a little prep work to get the functions to play nicely with Swift.
Unfortunately, Swift doesn’t work well with C union types. It simply can’t see things defined with a C union.
Thankfully, we can work around the problem by creating some wrappers. If you’ll add two new files to your project and implement them, you’ll be on your way. They are:
- pkcs7_union_accessors.h
- pkcs7_union_accessors.c
pkcs7_union_accessors.h implementation
|
|
pkcs7_union_accessors.c implementation
|
|
Bridging header updates
After you create the union accessor files, you need to update your project’s bridging header to import the new header file:
|
|
ReceiptExtractor struct implementation
Now it’s time to dive into the actual implementation of what I’m calling a ReceiptExtractor
. Have a look at the code with some explanatory comments following:
|
|
ReceiptExtractor struct explanation
Most of the code above is a Swift translation of what’s found at Objc.io’s Receipt Validation guide.
I did a little research over at the OpenSSL site though, and thought it might be helpful for the curious to know what some of these non-intuitive function names stand for and what they do.
BIO_new
for example. “BIO” stands for “Basic I/O”. It’s an abstraction over the underlying basic input and output operations that your app uses for cryptographic operations.
What we’re doing with BIO_new(BIO_s_mem())
is saying that we want a new Basic I/O mechanism that uses memory for its I/O operations.
BIO_write
takes the receiptData
that was located and loaded, and writes the entire length of its bytes to memory (the receiptBIO
that was created first).
To get the actual PKCS #7 container, the d2i_PKCS7_bio
function is used.
Once we have the container in hand, it’s a matter of making sure it has contents.
I couldn’t find a lot of information about the call to pkcs7_d_sign
, but the primary point of line 13 above is to get a “numerical identifier”, which is what “NID” stands for in OBJ_obj2nid
.
Digging into the PKCS #7 container, you can access the right property and convert it to a numerical identifier that you can check.
As long as the NID returned is equal to the NID_pkcs7_data
constant value, things are good. If they’re not, that means the receipt has no information and validation fails (thus, the guard and throw statement in lines 14-15).
If everything passes the guard, though, the PKCS #7 container is returned, and we’re ready for the next step of the receipt validation process, which is to verify the signature on the receipt with Apple’s root certificate. That, however, will happen in another entry to this series.
Until next time!
- Preparing to Test Receipt Validation for iOS
- Loading a Receipt for Validation with Swift
- OpenSSL for iOS & Swift the Easy Way
- Receipt Validation – Verifying a Receipt Signature in Swift
- Receipt Validation – Parse and Decode a Receipt with Swift
- Finalizing Receipt Validation in Swift – Computing a GUID Hash