-
Notifications
You must be signed in to change notification settings - Fork 450
Receipt verification
RMStore doesn't perform receipt verification by default but provides reference implementations. You can implement your own custom verification or use the reference verifiers provided by the library.
RMStore provides receipt verification via RMStoreAppReceiptVerifier
(for iOS 7 or higher) and RMStoreTransactionReceiptVerifier
(for iOS 6 or lower).
Neither is intended as a fail-proof solution and, if security is a real concern, you might want to avoid using an open source implementation for your receipt verification code. That said, the reference verifiers should provide a good starting point for your own implementation.
RMStoreAppReceiptVerifier
is the reference implementation for local app receipt verification in iOS 7. It uses OpenSSL to extract the receipt in ASN1 from the PKCS #7 container and then parse the ASN1 data into an object.
pod 'RMStore/AppReceiptVerificator'
The AppReceiptVerifier
subspec depends on the OpenSSL pod, which builds a static library from the OpenSSL. This might take a while the first time.
First, add the following files from RMStore/Optional to your project:
RMStore includes binaries and headers for the latest OpenSSL version at the time of writing RMStoreAppReceiptVerifier
. However, you might want to build OpenSSL yourself. See https://github.com/x2on/OpenSSL-for-iPhone for a build script.
Then, you will need to add OpenSSL as a static library. RMStore includes binaries and headers for the latest OpenSSL version at the time of writing RMStoreAppReceiptVerifier
. However, you might want to build OpenSSL yourself. See https://github.com/x2on/OpenSSL-for-iPhone for a build script.
To add OpenSSL as a static library:
- In Build Phases, add
libssl.a
andlibcrypto.a
to Link Binary With Libraries. - In Build Settings, add the headers folder to the Header Search Paths.
Check out RMStoreDemo as a reference.
Receipts are signed with the Apple Inc. Root Certificate. In order to verify the receipt signature, you need to include it in your app. The certificate can be downloaded from the Apple Root Certificate Authority.
By default, RMAppReceipt
will expect the certificate to be a resource named AppleIncRootCertificate.cer
. You can also set a custom url:
[RMAppReceipt setAppleRootCertificateURL:myCustomURL];
If no certificate is found, no signature verification will be performed. RMStoreAppReceiptVerifier
will still verify that the receipt corresponds to your app, version and has a valid hash.
To verify the app receipt RMStoreAppReceiptVerifier
checks the bundle identifier and bundle version contained within. Given that it is possible to modify the app bundle in jailbroken devices, you can provide hardcoded values for the comparison. For example:
_receiptVerifier.bundleIdentifier = @"net.robotmedia.test";
_receiptVerifier.bundleVersion = @"1.0";
RMStoreTransactionReceiptVerifier
is the reference implementation for transaction receipt verification in iOS 6 or lower. It gets a receipt from [SKPaymentTransaction transactionReceipt
] and then validates it against Apple's server. This method has been deprecated in iOS 7, and as such the verifier itself is deprecated.
pod 'RMStore/TransactionReceiptVerificator'
Add the following files from RMStore/Optional to your project:
RMStore delegates receipt verification, enabling you to provide your own implementation using the RMStoreReceiptVerifier
protocol:
- (void)verifyTransaction:(SKPaymentTransaction*)transaction
success:(void (^)())successBlock
failure:(void (^)(NSError *error))failureBlock;
Call successBlock
if the receipt passes verification, and failureBlock
in any other case. If verification could not be completed (e.g., due to connection issues), then error
must be of code RMStoreErrorCodeUnableToCompleteVerification
to prevent RMStore to finish the transaction.
No matter if you use a custom or reference verifier, you will need to set it at startup. For example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
const BOOL iOS7OrHigher = floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
_receiptVerifier = iOS7OrHigher ? [[RMStoreAppReceiptVerifier alloc] init] : [[RMStoreTransactionReceiptVerifier alloc] init];
[RMStore defaultStore].receiptVerifier = _receiptVerifier;
// Your code
return YES;
}
Bear in mind that receiptVerifier
is a weak property. In the above example the app delegate is responsible of retaining the verifier.
There is a known vulnerability in iOS 5.1 or lower related to app-side receipt verification. RMStore does not address this vulnerability. If you are using RMStoreTransactionReceiptVerifier
in iOS 5.x, please read this technical note.