- 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.
- You’ve extracted the PKCS #7 container.
The aim of this guide is to help you take a look inside the PKCS #7 container, and verify the presence and authenticity of the signature on the receipt.
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.
Accordingly, I’ve created a ReceiptLoader that finds the receipt on the file system and loads it into memory.
As of the last entry in this series of guides, I’ve also got a
ReceiptExtractor to extract the receipt contents from its PKCS #7 container.
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 two cases:
Preparation step: Download Apple’s root certificate
You need a copy of Apple’s root certificate in order to fully complete this phase of receipt validation.
How do you get a copy of it? Great question (with an answer)!
If you go to https://www.apple.com/certificateauthority/, you can get your hands on a copy by downloading the “Apple Inc. Root Certificate” file:
What can go wrong with receipt signature verification?
I’ll start off the code piece of this guide by asking, “What could go wrong?”. That’ll help define a few more
ReceiptValidationError cases, and might point us in a direction when it comes to implementing a new Type to use within the
Right off the bat, I can think of two or three things that could go awry at this stage of the receipt validation process:
1 – The receipt that we loaded may not be signed at all
2 – We don’t have a copy of Apple’s root certificate to validate the signature with
3 – The signature on the receipt is invalid because it doesn’t match against Apple’s root certificate
I’ll add those three new error states to the
ReceiptValidationError enum now:
Another step, another Type. This has been my strategy so far, so I’m stickin’ to it!
I’m validating the presence and authenticity of the signature on the receipt, so I picked the name
ReceiptSignatureValidator for this one.
When I identified three new
ReceiptValidationError cases earlier, I had in mind that they could potentially point me in a direction when implementing this new
What if this Type had two functions?
checkSignaturePresence, I’ll look, and if the receipt isn’t signed at all, I’ll throw the
receiptNotSigned Error case.
checkSignatureAuthenticity, I’ll look, and if the Apple root certificate is missing from the bundle for some reason, I’ll throw
appleRootCertificateNotFound. And if the signature on the receipt doesn’t jive with Apple’s root certificate, I’ll throw
Here’s the skeleton of the struct:
checkSignatureAuthenticity need to peek into the PKCS #7 container that encapsulates the receipt data, so each function asks for a reference to an
UnsafeMutablePointer<PKCS7> as one of its arguments.
If you’re following along with the series, you’ll be glad to know that the
ReceiptExtractor that we built previously has a method called
extractPKCS7Container that actually returns a
UnsafeMutablePointer<PKCS7>, so you can just use the a call to
extractPKCS7Container with the new
Now to actually implement
Checking signature presence
Checking for the presence of a signature is actually relatively simple. Take a look:
The PKCS #7 container has a type code associated with it if it’s signed. All we need to do is access that type code, and compare it against the
In order to be valid, the receipt must be signed, so I’ve implemented this as a guard.
Checking signature authenticity
Loading Apple’s root certificate
Now comes the part where we check whether the signature on the receipt is authentic or not.
First, we’ve got to load up Apple’s root certificate (assuming it exists in the app bundle). Here’s a function that can be nested inside of
ReceiptSignatureValidator to do the job:
This function guards against the absence of Apple’s root certificate. If it can’t be found in the main bundle, the function throws
appleRootCertificateNotFound. This error is obviously preventable, but hey – never hurts to protect yourself if you’re using this code in multiple projects and forget to grab a copy of Apple’s root certificate.
① As long as the .cer file exists in the app bundle, the next step is to create a new in-memory BIO (basic input-output) pointer. That’s what
let appleRootCertificateBIO = BIO_new(BIO_s_mem()) does.
② Next, we’ve got to write the contents of the certificate to memory so we can work with it:
BIO_write needs a location to write to, namely, our
It also needs to know what to write:
(appleRootCertificateData as NSData).bytes
Finally, it needs to know the length of the data to write:
③ Once that’s complete, we can obtain pointer to an
X509, which will be used for the next step: verifying the authenticity of the signature on the receipt with the x509 certificate from Apple’s root certificate authority.
let appleRootCertificateX509 = d2i_X509_bio(appleRootCertificateBIO, nil) gives us our return value!
Verifying signature authenticity
The final step is to take the
X509 pointer, and use it to verify the authenticity of the signature on the PKCS #7 Container.
Once again, here’s a function that can take both items and do the work:
① The X509 Store is what holds the information for verification, so we use
X509_STORE_new() to create one.
X509_STORE_add_cert function is used to prepare the X509 Store, and the X509 Certificate for verification purposes.
③ OpenSSL keeps an internal table of digest algorithms and ciphers. It uses this table to lookup ciphers via certain functions.
OpenSSL_add_all_digests() is called to load the necessary digest algorithms for verification.
④ The final step is to use the
PKCS7_verify function, passing it the PKCS #7 Container, and the x509 Certificate Store.
PKCS7_Verify will return 1 if the signature is valid. If
PKCS7_Verify returns any integer value other than 1, the signature is to be interpreted as invalid.
Putting it all together
Final ReceiptSignatureValidator implementation
The final version of the
ReceiptSignatureValidator looks like this:
Additions to ReceiptValidator
ReceiptValidator struct that’s been growing to accommodate each of the steps now looks like this (additions highlighted):
The final piece is to attempt to do something intelligent with any of the possible error conditions that could be included with the
ReceiptValidator validation result. Here’s a sample implementation at the call site for
validateReceipt() (probably in a view controller somewhere in your app:
In the case where the receipt signature is invalid, my only thought right now is to request a new receipt from the app store and attempt to re-validate it.
Wait, there’s more? In short, yes. We’ve made significant progress, but there’s still more work to be done if you want to fully validate a receipt for your app, or for an in-app purchase.
- Preparing to Test Receipt Validation for iOS
- Loading a Receipt for Validation with Swift
- OpenSSL for iOS & Swift the Easy Way
- Extracting a PKCS7 Container for Receipt Validation with Swift
- Receipt Validation – Parse and Decode a Receipt with Swift
- Finalizing Receipt Validation in Swift – Computing a GUID Hash