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.

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 NSData 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!

  • Chris Mills

    Please tell me you are nearly ready to release the next part to this series! You seem to have produced by far the best write up on this I’ve found so far and am keen to understand the next steps.

  • http://datacomlink.blogspot.co.id/ 陳大衛

    nice…

  • Kevin Maloles

    I’m ready for the next part! Very helpful!