iOS Receipt Validation Guide
iOS Receipt Validation Guide
Programming Guide
Contents
Receipt Fields 23
App Receipt Fields 23
Bundle Identifier 23
App Version 23
Opaque Value 24
SHA-1 Hash 24
In-App Purchase Receipt 24
Original Application Version 24
2
Contents
3
Figures, Tables, and Listings
4
About Receipt Validation
Note: This book was previously titled Validating Mac App Store Receipts .
The receipt for an application or in-app purchase is a record of the sale of the application and of any in-app
purchases made from within the application. You can add receipt validation code to your application to prevent
unauthorized copies of your application from running. Refer to the license agreement and the review guidelines
for specific information about what your application may and may not do to implement copy protection.
Receipt validation requires an understanding of cryptography and a variety of secure coding techniques. It's
important that you employ a solution that is unique to your application.
At a Glance
There are two ways to validate receipts: locally and with the App Store. Compare both approaches and determine
which is a better fit for your app and your infrastructure. You can also choose to implement both approaches.
Relevant Chapters: “Validating Receipts Locally” (page 6), “Receipt Fields” (page 23)
Relevant Chapters: “Validating Receipts With the App Store” (page 19), “Receipt Fields” (page 23)
5
Validating Receipts Locally
Perform receipt validation immediately after your app is launched, before displaying any user interface or
spawning any child processes. Implement this check in the main function, before the NSApplicationMain
function is called. For additional security, you may repeat this check periodically while your application is
running.
6
Validating Receipts Locally
Locate and Parse the Receipt
Note: In OS X, if the appStoreReceiptURL method is not available (on older systems), you can
fall back to a hardcoded path. The receipt’s path is /Contents/_MASReceipt/receipt inside the
app bundle.
In iOS, if the appStoreReceiptURL method is not available (on older systems), you can fall back
to validating the transactionReceipt property of an SKPaymentTransaction object with the
App Store. For details, see “Validating Receipts With the App Store” (page 19).
The receipt is a binary file with the structure shown in Figure 1-1.
Receipt
Payload
Attribute
...
Attribute
...
Attribute
...
Certificate chain
Signature
The outermost portion (labeled Receipt in the figure) is a PKCS #7 container, as defined by RFC 2315, with its
payload encoded using ASN.1 (Abstract Syntax Notation One), as defined by ITU-T X.690. The payload is
composed of a set of receipt attributes. Each receipt attribute contains a type, a version, and a value.
7
Validating Receipts Locally
Compute the Hash of the GUID
The structure of the payload is defined using ASN.1 notation in Listing 1-1. You can use this definition with
the asn1c tool to generate data type declarations and functions for decoding the payload, rather than writing
that part of your code by hand. You may need to install asn1c first; it is available through MacPorts and
SourceForge.
For information about keys found in a receipt, see “Receipt Fields” (page 23).
To generate the code, save the payload description shown in Listing 1-1 to a file and, in Terminal, run the
following command:
After the asn1c tool finishes generating files in the current directory, add the files it generated to your Xcode
project.
BEGIN
type INTEGER,
version INTEGER,
END
In iOS, use the value returned by the identifierForVendor property of UIDevice as the computer’s GUID.
8
Validating Receipts Locally
Validate the Receipt
To compute the hash, first concatenate the GUID value with the opaque value (the attribute of type 4) and the
bundle identifier. Use the raw bytes from the receipt without performing any UTF-8 string interpretation or
normalization. Then compute the SHA-1 hash of this concatenated series of bytes.
Note: Bundle identifiers and version identifier strings are UTF-8 strings, not just a series of bytes.
Make sure you code your comparison logic accordingly.
If your app supports the Volume Purchase Program, check the receipt’s expiration date.
9
Validating Receipts Locally
Set a Minimum System Version for Mac Apps
If the system successfully obtains a valid receipt, it relaunches the application. Otherwise, it displays an error
message to the user, explaining the problem.
Do not display any error message to the user if validation fails. The system is responsible for trying to obtain
a valid receipt or informing the user that the receipt is not valid.
Do not try to terminate the app. At your option, you may give the user a grace period or restrict functionality
inside your app.
10
Validating Receipts Locally
Test During the Development Process
● Avoid simple code constructions that provide a trivial target for patching the application binary.
For example, avoid writing code like the following:
if (failedValidation) {
exit(173);
With this development receipt installed, you can launch your application by any method—for example, with
gdb or the Xcode debugger.
11
Validating Receipts Locally
Validate In-App Purchases
If the receipt is not valid, none of the in-app purchases are valid.
2. Parse the in-app purchase receipts (the values for the attributes of type 17).
Each in-app purchase receipt consists of a set of attributes, like the application’s receipt does. The structure
for these receipts is defined in Listing 1-2 (page 12). As when parsing the receipt, you can generate some
of your code from the ASN.1 description using the asn1c tool. Ignore all attributes with types that do not
appear in the table—they are reserved for use by the system and their contents may change at any time.
For information about the fields in a receipt, see “Receipt Fields” (page 23).
3. Compare the product identifier in question to the product identifier of each in-app purchase receipt.
If there is a receipt that matches, validation succeeds. Otherwise, validation fails.
When validation succeeds, your application enables the purchased functionality—for example, by downloading
content or adding features. When validation fails, your application simply does not enable the functionality.
type INTEGER,
version INTEGER,
The attributes for the original transaction identifier and original transaction date are used when a purchase is
redownloaded. The redownloaded purchase is given a new transaction identifier, but it contains the identifier
and date of the original purchase.
12
Validating Receipts Locally
Implementation Tips
Consumable Products and Non-Renewing Subscriptions: The in-app purchase receipt for a
consumable product or a non-renewing subscription is added to the receipt when the purchase is
made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed
from the receipt the next time the receipt is updated—for example, when the user makes another
purchase or if your app explicitly refreshes the receipt.
Implementation Tips
This section contains several code listings for your reference as you implement receipt validation.
#import <IOKit/IOKitLib.h>
#import <Foundation/Foundation.h>
CFDataRef copy_mac_address(void)
kern_return_t kernResult;
mach_port_t master_port;
CFMutableDictionaryRef matchingDict;
io_iterator_t iterator;
io_object_t service;
if (kernResult != KERN_SUCCESS) {
return nil;
13
Validating Receipts Locally
Implementation Tips
if (!matchingDict) {
return nil;
if (kernResult != KERN_SUCCESS) {
while((service = IOIteratorNext(iterator)) != 0) {
io_object_t parentService;
&parentService);
if (kernResult == KERN_SUCCESS) {
if (macAddress) CFRelease(macAddress);
} else {
IOObjectRelease(service);
IOObjectRelease(iterator);
return macAddress;
14
Validating Receipts Locally
Implementation Tips
If you use OpenSSL, statically link your binary against it. Dynamic linking against OpenSSL is deprecated and
results in build warnings.
Make sure your code does the following as outlined in the listings:
1. Verify the signature (Listing 1-4 (page 15)).
2. Parse the payload (Listing 1-5 (page 16)).
3. Extract the receipt attributes (Listing 1-6 (page 17)).
4. Compute the hash of the GUID (Listing 1-7 (page 17)).
/* The PKCS #7 container (the receipt) and the output of the verification. */
BIO *b_p7;
PKCS7 *p7;
/* The Apple root certificate, as raw data and in its OpenSSL representation. */
BIO *b_x509;
X509 *Apple;
/* ... Initialize both BIO variables using BIO_new_mem_buf() with a buffer and its
size ... */
/* Initialize b_out as an output BIO to hold the receipt payload extracted during
signature verification. */
/* Capture the content of the receipt file and populate the p7 variable with the
PKCS #7 container. */
p7 = d2i_PKCS7_bio(b_p7, NULL);
15
Validating Receipts Locally
Implementation Tips
/* Initialize b_x509 as an input BIO with a value of the Apple root certificate and
load it into X509 data structure. Then add the Apple root certificate to the
structure. */
X509_STORE_add_cert(store, Apple);
/* Verify the signature. If the verification is correct, b_out will contain the
PKCS #7 payload and rc will be 1. */
int rc = PKCS7_verify(p7, NULL, store, NULL, b_out, 0);
/* For additional security, you may verify the fingerprint of the root certificate
and verify the OIDs of the intermediate certificate and signing certificate. The
OID in the certificate policies extension of the intermediate certificate is (1
2 840 113635 100 5 6 1), and the marker OID of the signing certificate is (1 2 840
113635 100 6 11 1). */
size_t pld_sz;
/* Variables used to parse the payload. Both data types are declared in Payload.h.
*/
asn_dec_rval_t rval;
/* ... Load the payload from the receipt file into pld and set pld_sz to the payload
size ... */
/* Parse the buffer using the decoder function generated by asn1c. The payload
variable will contain the receipt attributes. */
16
Validating Receipts Locally
Implementation Tips
/* Iterate over the receipt attributes, saving the values needed to compute the
GUID hash. */
size_t i;
entry = payload->[Link][i];
switch (entry->type) {
case 2:
bundle_id = &entry->value;
break;
case 3:
bundle_version = &entry->value;
break;
case 4:
opaque = &entry->value;
break;
case 5:
hash = &entry->value;
break;
17
Validating Receipts Locally
Implementation Tips
size_t guid_sz;
EVP_MD_CTX evp_ctx;
EVP_MD_CTX_init(&evp_ctx);
UInt8 digest[20];
/* Compute the hash, saving the result into the digest variable. */
18
Validating Receipts With the App Store
Note: There is a vulnerability in iOS 5.1 and earlier related to receipt validation with the app store
directly from a device, without using a server. For more details and a mitigation strategy, see In-App
Purchase Receipt Validation for iOS 5.1 and Earlier .
Use a trusted server to communicate with the App Store. Using your own server lets you design your app to
recognize and trust only your server, and lets you ensure that your server connects with the App Store server.
It is not possible to build a trusted connection between a user’s device and the App Store directly because you
don’t control either end of that connection.
Communication with the App Store is structured as JSON dictionaries, as defined in RFC 4627. Binary data is
base64 encoded, as defined in RFC 4648.
19
Validating Receipts With the App Store
Send the Receipt Data to the App Store
Key Value
password Only used for iOS 6 style transaction receipts for auto-renewable subscriptions. Your
app’s shared secret (a hexadecimal string).
Submit this JSON object as the payload of an HTTP POST request. In the test environment, use
[Link] as the URL. In production, use
[Link] as the URL.
NSError *error;
NSDictionary *requestContents = @{
};
options:0
error:&error];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
20
Validating Receipts With the App Store
Parse the Response
if (connectionError) {
} else {
NSError *error;
}];
Key Value
status Either 0 if the receipt is valid, or one of the error codes listed in Table
2-1 (page 22).
For iOS 6 style transaction receipts, the status code reflects the status of the
specific transaction’s receipt.
For iOS 7 style app receipts, the status code is reflects the status of the app
receipt as a whole. For example, if you send a valid app receipt that contains
an expired subscription, the response is 0 because the receipt as a whole is
valid.
receipt A JSON representation of the receipt that was sent for verification. For
information about keys found in a receipt, see “Receipt Fields” (page 23).
latest_receipt Only returned for iOS 6 style transaction receipts for auto-renewable
subscriptions. The base-64 encoded transaction receipt for the most recent
renewal.
latest_receipt_info Only returned for iOS 6 style transaction receipts for auto-renewable
subscriptions. The JSON representation of the receipt for the most recent
renewal.
21
Validating Receipts With the App Store
Parse the Response
21000 The App Store could not read the JSON object you provided.
21004 The shared secret you provided does not match the shared secret on file for your
account.
Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21006 This receipt is valid but the subscription has expired. When this status code is returned
to your server, the receipt data is also decoded and returned as part of the response.
Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21007 This receipt is from the test environment, but it was sent to the production environment
for verification. Send it to the test environment instead.
21008 This receipt is from the production environment, but it was sent to the test environment
for verification. Send it to the production environment instead.
The values of the latest_receipt and latest_receipt_info keys are useful when checking whether an
auto-renewable subscription is currently active. By providing any transaction receipt for the subscription and
checking these values, you can get information about the currently-active subscription period. If the receipt
being validated is for the latest renewal, the value for latest_receipt is the same as receipt-data (in
the request) and the value for latest_receipt_info is the same as receipt.
22
Receipt Fields
Receipts are made up of a number of fields. Some fields are only available locally, in the ASN.1 form of the
receipt, or only when validating with the App Store, in the JSON form of the receipt. Keys not documented
below are reserved for use by Apple and must be ignored by your app.
Bundle Identifier
The app’s bundle identifier.
App Version
The app’s version number.
23
Receipt Fields
App Receipt Fields
Opaque Value
An opaque value used, with other data, to compute the SHA-1 hash during validation.
SHA-1 Hash
A SHA-1 hash, used to validate the receipt.
In the JSON file, the value of this key is an array containing all in-app purchase receipts. In the ASN.1 file, there
are multiple fields that all have type 17, each of which contains a single in-app purchase receipt.
24
Receipt Fields
In-App Purchase Receipt Fields
Receipts prior to June 20, 2013 omit this field. It is populated on all new receipts, regardless of OS version. If
you need the field but it is missing, manually refresh the receipt using the SKReceiptRefreshRequest class.
This key is present only for apps purchased through the Volume Purchase Program. If this key is not present,
the receipt does not expire.
When validating a receipt, compare this date to the current date to determine whether the receipt is expired.
Do not try to use this date to calculate any other information, such as the time remaining before expiration.
Quantity
The number of items purchased.
25
Receipt Fields
In-App Purchase Receipt Fields
This value corresponds to the quantity property of the SKPayment object stored in the transaction’s payment
property.
Product Identifier
The product identifier of the item that was purchased.
This value corresponds to the productIdentifier property of the SKPayment object stored in the
transaction’s payment property.
Transaction Identifier
The transaction identifier of the item that was purchased.
26
Receipt Fields
In-App Purchase Receipt Fields
All receipts in a chain of renewals for an auto-renewable subscription have the same value for this field.
Purchase Date
The date and time that the item was purchased.
For a transaction that restores a previous transaction, the purchase date is the date of the restoration. Use
“Original Purchase Date” (page 27) to get the date of the original transaction.
In an auto-renewable subscription receipt, this is always the date when the subscription was purchased or
renewed, regardless of whether the transaction has been restored.
In an auto-renewable subscription receipt, this indicates the beginning of the subscription period, even if the
subscription has been renewed.
27
Receipt Fields
In-App Purchase Receipt Fields
Cancellation Date
For a transaction that was canceled by Apple customer support, the time and date of the cancellation.
Treat a canceled receipt the same as if no purchase had ever been made.
App Item ID
A string that the App Store uses to uniquely identify the application that created the transaction.
If your server supports multiple applications, you can use this value to differentiate between them.
Apps are assigned an identifier only in the production environment, so this key is not present for receipts
created in the test environment.
28
Receipt Fields
In-App Purchase Receipt Fields
This key is not present for receipts created in the test environment.
29
Document Revision History
Date Notes
2011-07-07 New document that describes how an application can validate its receipt.
30
Apple Inc.
Copyright © 2014 Apple Inc.
All rights reserved.