VerifyStoreReceiptiOS icon indicating copy to clipboard operation
VerifyStoreReceiptiOS copied to clipboard

Upcoming changes to the App Store receipt signing certificate

Open Paco777 opened this issue 3 years ago • 7 comments

Hi, With the upcoming changes to the App Store receipt signing certificate (see https://developer.apple.com/news/?id=ytb7qj0x), could anyone confirm the the code from this repository will still work correctly ? Thank you.

Paco777 avatar Jan 09 '23 12:01 Paco777

First, thanks for pointing out that news article. I hadn't seen that before.

The relevant portion of that news article states:

If you validate App Store receipts on device using the App Store receipt signing certificate, make sure you haven’t hardcoded the intermediate certificate and verify that the chain of trust for the container’s signature matches the Apple Inc. Root Certificate.

This project does not use any intermediate certificate. This project only expects the Apple Inc. root certificate (which is not actually included in this project but must be downloaded by you and added to your project).

While I'm not 100% positive, It does not seem like the change mentioned in Apple's news article will impact the functionality of this project.

The relevant portion of this project is in VerifyStoreReceipt.m in the method dictionaryWithAppStoreReceipt. I was not the original author of that code but that code only makes use of the root certificate and not any intermediate certificate (that I can see).

All of my apps in the App Store perform receipt validation using code based on this project. I guess I'll know for sure in about a week if I start getting support emails from my customers.

rmaddy avatar Jan 12 '23 19:01 rmaddy

Thanks a lot for your feedback. I also assume there shouldn't be any issues, but it's always good to have a second thought. I'll let you know if I notice something wrong.

Best regards

Paco777 avatar Jan 13 '23 13:01 Paco777

I’m using this code in a project and began running into issues yesterday (the date Apple mentioned in the article) where PKCS7_verify within the dictionaryWithAppStoreReceipt function is erroring out with a certificate verification error. Have you seen any problems using this code? Adding the argument PKCS7_NOVERIFY to the call results in the receipt being parsed, so it appears to be a chain verification error.

wibs avatar Feb 08 '23 22:02 wibs

I just made some tests with my app, and in sandbox mode, with a test user, i don't have any problems (yet). I use obtainInAppPurchases to know if a specific purchase has already been made once, and I can get them without any errors. PKCS7_verify currently returns 1 as expected.

Paco777 avatar Feb 08 '23 22:02 Paco777

I’m on holiday out of the country without my computer so I can’t do any research on this issue for a bit over a week. If anyone has a chance to try using the new certificate from Apple, please report the results. Thanks

rmaddy avatar Feb 09 '23 08:02 rmaddy

It looks like PKCS7_verify does fail on expired intermediate certs. So any App Store receipt that has been signed with the now expired cert, will not be validated. That means all receipts issued before January 18 are considered invalid.

New receipts signed with the updated intermediate cert (valid until 2030) will succeed.

Users need to get a fresh receipt from the App Store, otherwise the validation will fail. (e.g. Restore Purchase or remove+redownload app)

floorish avatar Feb 10 '23 10:02 floorish

According to https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1

Many cryptographic libraries default to using the device’s current time and date when validating a PKCS7 package, but this may not produce the correct results when validating a receipt’s signature. For example, if the receipt was signed with a valid certificate, but the certificate has since expired, using the device’s current date incorrectly returns an invalid result.

Therefore, make sure your app always uses the date from the Receipt Creation Date field to validate the receipt’s signature.

So I guess the correct way to verify the receipt is to set the date first:

[...]
X509_STORE_add_cert(store, appleCA);

// set correct date for verification
let creationDate: Date = <Receipt Creation Date - ASN.1 Field Type 12>
let param = X509_STORE_get0_param(store)
X509_VERIFY_PARAM_set_time(param, time_t(creationDate.timeIntervalSince1970))
            
verifyReturnValue = PKCS7_verify(p7,NULL,store,NULL,payload,0);
[...]

Alternatively you can disable time verification entirely, but I'm unsure about the security implications:

let param = X509_STORE_get0_param(store)
X509_VERIFY_PARAM_set_flags(param, UInt(X509_V_FLAG_NO_CHECK_TIME))

floorish avatar Feb 10 '23 15:02 floorish