Introduction: Recently, WE did StoreKit2 on OC SDK, which followed our original delivery process. There are three steps in the delivery process, and the last step is: after the user paid, we received a successful callback, and we applied for delivery, but now we often returned errors. Later, we used simple Demo to debug and found that the problem was not simple.

Check out the documentation: We lost the appStoreReceiptURL receipt, which couldn’t be updated in StoreKit2 (see the WWDC2021 video later).

However, we need to verify the authenticity of the receipt for shipping in the background, which leads us to use StoreKit2 and have to update our shipping logic to verify the new receipt.

Into the Apple

The official information available is as follows

Apple’s signature verification diagram, which the video doesn’t explain much about

Wwdc21-10174 Manage in-app purchases on your server

In the video above, Apple explains how to validate signatures, but it's pretty vague when we're familiar with the validation process.

The specific data format of JWS will be explained below.


Some parameters left on the API documentation

@available(iOS 15.0.macOS 12.0.tvOS 15.0.watchOS 8.0.*)
extension VerificationResult where SignedType= =Transaction {

    /// The raw JSON web signature for the signed value.
    public var jwsRepresentation: String { get }

    /// The data for the header component of the JWS.
    public var headerData: Data { get }

    /// The data for the payload component of the JWS.
    public var payloadData: Data { get }

    /// The data for the signature component of the JWS.
    public var signatureData: Data { get }

    /// The signature of the JWS, converted to a `CryptoKit` value.
    public var signature: P256.Signing.ECDSASignature { get }

    /// The component of the JWS that the signature is computed over.
    public var signedData: Data { get }

    /// The date the signature was generated.
    public var signedDate: Date { get }

    /// A SHA-384 hash of `AppStore.deviceVerificationID` appended after
    /// `deviceVerificationNonce` (both lowercased UUID strings).
    public var deviceVerification: Data { get }

    /// The nonce used when computing `deviceVerification`.
    /// - SeeAlso: `AppStore.deviceVerificationID`
    public var deviceVerificationNonce: UUID { get}}Copy the code
The JWS data format obtained from WWDC is as follows:

Base64(header) + "." + Base64(payload) + "." + sign( Base64(header) + "." + Base64(payload) )

// Apple actually returns more than 5,000 characters. The following is a short space to omit a wave of eyJh-- long, long, long -- long, long, long -- kxNH0Copy the code
  • It’s basically Header, Payload, Signture
  • Header = Base64(Header), if you parse Base64 you’ll get
header: {
    alg: 'ES256',// AlG claims to know what signature algorithm we use
    x5c: [ // Certificate chain in array of x5c declaration'MIIEMDueU3... ', 'MII... ', 'XXX... xx' ] }Copy the code
  • Payload = Base64(Payload
/ / jwsRepresentationJWS parsed
{
    "transactionId":"1000000916922942"."originalTransactionId":"1000000916922942"."bundleId":"com.xxx.ios"."productId":"king.xxxx.60"."purchaseDate":1637723816809."originalPurchaseDate":1637723816809."quantity":1."type":"Consumable"."deviceVerification":"qVh9F+9eGf9KQxxxxxxpPrfcGdlJyht775ID9ytSQCWItx"."deviceVerificationNonce":"a8735bcf-825f-4aeb-b99c-6f866cadc96e"."appAccountToken":"397711d6-61b8-bfb7-c94f-8xxxxxdb7b7"."inAppOwnershipType":"PURCHASED"."signedDate":1637723816914
}
Copy the code
  • Signture is the signature of the Base64 header and payload
    1. Use header to obtain alG algorithm and X5C certificate to decrypt signture signature (Use your favorite password library to decrypt the signature of the transaction information)
    1. A CA certificate may be required to verify the authenticity of the X5C certificate chain.
    1. Verify decryption capture is obtainedsigntureValue after decryption with Base64payloadVerify.

To summarize, if you just want to get the specific parameters in the transaction, you can just base64 Decode Payload, but if you need to validate the signature, you must use Signture, Header

However, there are several doubts and relevant information has not been found:

1. Alg and X5c we got, but what does the favorite password library mean? How exactly do we decrypt it? 2. After consulting the data, I found that X5C verification requires a root certificate, which Apple did not provide. 3. Perhaps in order to recommend the client self-verification method, Apple provides the above information, but does not disclose the information of other servers to verify the signature and sample code.Copy the code

I started complaining about jet lag and contacting Apple tech support (much more comfortable than the review team)

Not sure which root certificate to use for signature verification, contact the Apple PKI team:

2021-12-01

Dear Apple PKI team:

Good morning! I have been troubled by this problem for many days, I beg you to help me

Where can I download the Apple StoreKit 2 root certificate to install on my PHP server to verify StoreKit 2 transactions and make sure the JWS certificate chain comes from Apple?

Regrads

2021-12-03

Hello Ray,

Our certificates are located here – www.apple.com/certificate…

Apple Root G3 Apple Root CA – G3 Root.

Regards, Apple PKI

I now believe you know which certificate to choose for validationCopy the code

Below is how I use code-level Support to contact the technical staff

Code-level Support is $99 twice a year

Send: On November 29, 2021

PLATFORM AND VERSION iOS macOS 12.0.1 Xcode 13.1 iPhone 12 mini

DESCRIPTION OF PROBLEM

Dear Apple developer team:

Hi, good afternoon~ This is My case ID: XXXX. And my question description:

Hello! Recently we encountered a problem while trying out some new features of StoreKit2. Because we used a background The verification method to verify receipts (developer.apple.apple.com/documentati…). . but we found that we were unable to verify the legitimacy and authenticity of the receipt. (stackoverflow.com/questions/6…). , but it didn’t work, we don’t know which one certificates will actually work, so I want to know How to verify the legitimacy of the jwsReceipt in StoreKit2 on the server?

Hi, good afternoon ~ this is my case ID: XXXXX. Description of my problem:

Hello! We recently ran into a problem trying out some of StoreKit2’s new features. Because we used background authentication to verify the receipt apple internal Purchase server API, but we found that we could not verify the validity and authenticity of the receipt. Stackoverflow, but it didn’t work, we don’t know which certificate will actually work, so I wonder how to validate jwsReceipt in StoreKit2 on the server?

STEPS TO REPRODUCE

  1. Start Purchase
  2. entered the AppleId Sercet
  3. completed the purchase
  4. get Transaction value, e.g:displayPrice, jwsRepresentation
  5. get what I want from jsonRepresentation
  6. but idk jwsString legitimacy or truly from Apple?
  7. How Can I know about it?

Regrads Ray.

Ray: It mainly describes the problems we encountered when using this function, and the confusion we had to verify the receipt, as well as the corresponding steps. By Posting links to the materials we looked up, the technical staff can have a clearer understanding of our needs, which will be explained in the following reply.

Received On November 30, 2021

Hello Ray,

You stated – “Recently we encountered a problem while trying out some new features of StoreKit2. Because we used a Background verification method to verify receipts (developer.apple.apple.com/documentati…). , but we found that we were unable to verify the legitimacy and authenticity of the receipt.

When you mention the term receipt – are you referencing the appStoreReceiptURL – or are you referencing the verification result which comes with a completed StoreKit 2 transaction. Let mejust be clear, the appStoreReceiptURL remains a part of all App Store apps, both macOS, iOS, tvOS and watchOS. On the other hand, with StoreKit 2, verifying the transaction can be managed using the checkVerified method. This method provides a much simpler method to verify the legitimacy of a transaction compared to what was provided by validating the appStoreReceipt for a StoreKit 1 transaction.

Are you trying to do more with the verification result? Please explain in more detail what you want to achieve.

Translation: You said – “Recently we had a problem trying out some of StoreKit2’s new features. Because we used a background validation method to validate the receipt API, we found that we could not verify the validity and authenticity of the receipt. “

When you refer to the term receipt – are you referring to appStoreReceiptURL – or are you referring to the validation results that come with completed StoreKit 2 transactions. Let me be clear, appStoreReceiptURL is still part of all App Store apps, including macOS, iOS, tvOS and watchOS. On the other hand, in StoreKit 2, you can use the checkVerified method to verify transactions. This method provides a simpler way to verify the validity of a transaction than the one provided by appStoreReceipt, which validates StoreKit 1 transactions.

Do you want to do more with the validation results? Please be more specific about what you want to achieve.

Sincerely Yours,

Rich Kubota

Apple Inc

One Apple Park Way, MS 122DEF

Cupertino, CA 95014

USA

Apple informed StoreKit2 that there was an API in there to validate receipts on the client side, but what we needed was server validation. He probably didn't understand what I meant, so I needed to explain my requirements further

Continue to send

Judging from the reply, Apple didn't quite understand what I meant, so they reworded it this time and restated what I wanted with codes and English annotations.

2021-12-02

Hello Rich,

First of all, thank you for explaining the appStoreReceiptURL for me. I understand this point and it feels good to use it. At the same time, thank you very much for explaining StoreKit2. The checkVerified method is very good. But we are a game company. The high income and a lot fraudulent players make us have to use server verification. However, we have not found out how to perform server verification in the official documents and sample codes downloaded from WWDC. Below I will use the code to give a detailed example of our needs;

First of all, thank you for explaining appStoreReceiptURL to me. I understand that. It feels good to use. And thank you very much for explaining StoreKit2. The checkVerified method is very good. But we are a gaming company. High revenue and a large number of fraudulent players forced us to use server verification. However, in the official documentation and sample code downloaded from WWDC, we did not find out how to do server validation. I’ll use code to illustrate our requirements in detail.

//completed the IAP,Waiting for purchaseResult success or Failure guard let result = await purchase(toGetPurchaseResult:  product) else { return } //When I get PurchaseResult, I dont' want `checkVerified` method //I need send some paraments to my server,and which paraments that ensure the server  verifies the receipt? switch result { case .success(let verificationResult): // POST with some params to Server, Whether to include `jwsRepresentation` or more parameters? // The server tells me whether this order is real // deliver to user // finish transactionCopy the code

In summary, I have three questions about StoreKit2:

  1. What parameters do I need to transmit to the server to ensure that the receipt is verified? // What parameters do I need to pass to the server to make sure it validates the signature?
  2. How to verify when the server gets these receipts? // How does the server validate?
  3. If it contains JWS data, how should the server verify the authenticity of JWS? // How do I verify that JWS is true?

Reply received < friends with plenty of time can take a good look >

The 2021-12-03 04:11

Hello Ray,

Something to understand with StoreKit2, purchased transactions are not updated to the appStoreReceipt as are StoreKit1 transactions. When the app is notified of a successful transaction – a signed transaction (receipt) is returned with the transaction indication. The best place to see a description of this is in the 2021 WWDC presentation “Manage in-app purchases on your server”. A transcript is avaiable with the video. Search for the string

“Additionally, with StoreKit 2, we’re introducing new signed transactions in a JWS, or JSON web signature” Based on your question below, it appears that you hve a desire to validate the signed transaction signature section. Per the presentation

Verifying the signature is an option for you to validate that the transaction came from Apple and is trustworthy. If you only want to see the contents of the transaction, this step is not required. However, to verify the signature, you will need to use the claims available in the header portion of the signed transaction info. Use the alg claim to know what signing algorithm we used, and use the certificate chain in the array in the x5c claim. Once you have these two things, you can use your favorite cryptographic library to verify the signature of the signed transaction info.

If you are looking for sample code to manage this, I don’t have such a sample. This would be an enhancement request which you can submit using the Apple Developer Feedback Assistant web page. One question which I’m looking into – ” If the StoreKit2 app makes the checkVerified call, why might the app want to also validate the signature portion of the signed transaction.”

Translation:

The understanding of StoreKit2 is that purchased transactions are not updated to appStoreReceipt like StoreKit1 transactions. When the application receives notification of a successful transaction – the signed transaction (receipt) with the transaction instruction is returned. The best place to view this description is the 2021 WWDC presentation “Manage In-App Daylight on Your Server.” Video transcripts are available. The Command + F search ‘Transcript'”

Also, with StoreKit 2, we will introduce new signed transactions in JWS or JSON network signatures. “Based on your question below, it seems that you want to validate the signed part of the transaction. According to the

According to the demo, “Verification signatures are an option for you to verify that a transaction is from Apple and is trusted. This step is not required if you only want to view the contents of the transaction. However, to verify the signature, you will need to use the declaration, transaction information available in the title section of the signature. Use the ALG declaration to know what signature algorithm we used, and use the certificate chain from the array in the X5C declaration. Once you have those two things, you can use your favorite password library to verify the signature of the signed transaction information.”

If you’re looking for sample code to manage this, I don’t have one. This will be an enhancement to the request you can submit using the Apple Developer Feedback Assistant web page. A question I am investigating – “If an SK2 application makes a checkVerified call, why does the application want to verify the signed part of a signed transaction

From here, we know that WWDC does mention that API calls in Storekit2 are not updated to appStoreReceipt, and reminds us that headers are important, but we don't understand what a certificate chain is. At the same time, the technical staff also expressed his confusion, why the server for complex verification

Then, three hours later, another email arrived

The 2021-12-03 04:11

Hello Ray,

I now have a better explanation between the verificationResult method and the signed Transaction (receipt) associated with every successful transaction. The verificationResult method provides information for app use as to the validity of the transaction. However, given that the app might be running under a jailbroken environment, the app process can take an additional step to prevent a jailbreak from simply indicating that the tranaction is valid – by sending the signed transaction to your server for processing of the signature. Such action takes place on your server. The server receives the signed transaction information then processes the signature portion of the signed transaction as described in the WWDC Session video which I referenced in my earlier response – “Managing In-App Purchases on your server”

However, to verify the signature, you will need to use the claims available in the header portion of the signed transaction info. Use the alg claim to know what signing algorithm we used, and use the certificate chain in the array in the x5c claim. Once you have these two things, you can use your favorite cryptographic library to verify the signature of the signed transaction info.

The above process is a serverside issue to implement. Im asked for a sampleode reference. I'm not sure whether one exists, but is so I'll send a reference if I her of one. If I learn of additional information on this subject, I’ll respond. In the meantime, I suggest that you aubmit feedback asking for more information as to the algorithm possibilities and a sample code implementation.

Translation:

I now have a better explanation between the verifyResult method and the signed transaction (receipt) associated with each successful transaction. The verifyResult method provides the application with information about the validity of the transaction. However, given that the application may be running in a jailbroken environment, the application process can take additional steps to prevent the jailbreak from merely indicating that the transaction is valid – by sending the signed transaction to your server for signature processing. This type of operation occurs on your server. The server receives the signed transaction information and then processes the signed portion of the signed transaction as described in the WWDC session video CITED in my previous response – “#Manage In-app daylight assault on your server”

However, to verify the signature, you will need to use the declarations available in the Header section of the signed transaction information. Use the ALG declaration to know what signature algorithm we use, and use the certificate chain in the array declared by X5C. Once you have those two things, you can use your favorite password library to verify the signature of the signed transaction information.

The above procedure is to implement the server side problem. I asked for sample references. I’m not sure if it exists, but if I were her, I’d send a reference. If I learn additional information on this subject, I will reply. In the meantime, I encourage you to submit feedback requesting more information about algorithm possibilities and sample code implementations.

This is basically a repeat of the WWDC video, but it gives a bit of hope that there is a possibility of providing some sample code, and points to why we want server authentication: to prevent man-in-the-middle attacks from tampering with data and causing asset loss. This certificate chain is important, as I'll show you later.

Send E-mail

(After this document is sent, an Apple PKI reply is received. The Root certificate is in Apple Root G3 Apple Root CA-g3 Root.

2021-12-03

Dear Rich,

You stated “to verify the signature, you will need to use the claims available in the header portion of the signed transaction info. Use the alg claim to know what signing algorithm we used, and use the certificate chain in the array in the x5c claim . Once you have these two things, You can use your favorite cryptographic library to verify the signature of the signed transaction info.”

Yes, after I communicated with my colleagues in detail and watched the WWDC2021 video, we decided that we need a sample code to show me how to use the algsigning algorithm and x5ccertificate chain to verify the signature. According to my serverside colleague , this will also use the root certificate. We don’t know where to get the root certificate (if needed, please tell me where to get this certificate), if the root certificate is not needed If so, please use a complete sample code to solve my question.

If you don’t have sample code, please recommend your colleagues to tell me how to solve this problem such as specific thinking steps and effective links on the Internet, such as: Developer Forum

Best Regards, Ray

Dear Rich Kubota,

You say “To verify the signature, you need to use the declaration available in the header section of the signed transaction information. Use the ALG declaration to learn about the signature algorithm we use, and use the certificate chain in the array “in the X5C declaration. Once you have those two things, you can use your favorite crypto library to verify the signature of the signed transaction message.”

Yes, after detailed communication with colleagues and watching the WWDC2021 video, we decided that we needed a sample code to demonstrate how to use alG signature algorithm and X5C certificate chain to verify signatures. According to the server side of my colleagues, this also would use the root certificate, don’t know where to get the root certificate (if you need, please tell me where to get the certificate), if you don’t need root certificate, if yes, please use the complete sample code to solve my problem, if you don’t have the sample code, please recommend your colleagues tell me how to solve this problem, Specific thinking steps and useful links on the web, such as developer forums

Ray

On Friday, two replies came in

Dear Ray,

I will present your issues to may App Store Seerver contacts. Please understand that this is an informal method for handling this request. The preferred method whih I suggest that youfollow is to submit a API and documentation enhancement request that such information be provided by Apple. There is no direct sample which I'm aware of as the issue is for a server - not for a macOS, iOS or watchOS device.

To submit the API enhancement request, please use the Apple Developer Feedback Assistant web site. By submitting the feedback, you will be generating a document that is accessible to all of Apple. You comment here have limited scope in that only members of Developer Relations have access to your comments. By submitting the enhancement request, I can forward the reference company wide.

In the meantime, I will see what other details I can find. If I do so, I will pass them along.

Translation:

I will refer your questions to possible App Store Seerver contacts. Please understand that this is an informal way to handle this request. The preferred approach I recommend you follow is to submit API and document enhancement requests, with Apple providing such information. There are no direct examples THAT I know of, because the problem is server-specific – not macOS, iOS, or watchOS devices.

To submit an API enhancement request, use the Apple Developer Feedback Assistant Web site. By submitting feedback, you generate a document that all Apple users can access. The scope of your comments here is limited, as only members of the developer relationship can access your comments. By submitting enhanced requests, I can forward references across the company.

In the meantime, I’ll see what other details I can find. If I do, I’ll pass them on.

Hello Ray,

I’ve heard back from StoreKit engineering. With regards to understanding the validation of signed transaction signatures, a good place to start is the JSON Web Tokens web site. As for Apple documentation on this subject, the team understands the need for such and there is work in process. As for when the documentation will be available, I don’t have an answer. I still suggest that you submit a documentation enhncement request after reviewing the JSON Web Token site to ensure that any specific issues are addressed in Apple Developer documentation.

Translation:

I received an engineering response from StoreKit. A good starting point for understanding validation of signed transaction signatures is JSON Web Tokens Web Site. As for Apple’s documentation on this topic, the team understands the need for such documentation and is working on it. I don’t have an answer as to when the documentation will be available. I still recommend submitting a document enhancement request after viewing the JSON Web Token site to ensure that any specific issues are addressed in the Apple Developer documentation.

Before, I was just checking the data on JWT and never thought of using his library. Later, I used Go and PHP for decryption and verification, but got no good results.

Hello Kubota,

When I use the certificate to verify the signature, I get an error: expected key of size 256 bits, but was given 384 bits. Then I found the Apple Root CA – G3 Root given by Apple PKI , is: Public-Key: And our alg was ES256. I don’t know if it’s the problem with my code or I was wrong from the start of the certificate, so I desperately need your help. I contacted feedbackassistant.apple.com/, but it did not help me.

Translation:

When I used the certificate to verify the signature, I received an error: “Expected 256 bits of key, but got 384 bits”. Then I found the Apple Root ca-g3 Root, which is: public-key: (384 bit). Our algorithm is ES256. I don’t know whether it’s my code or I made a wrong certificate at the beginning, so I really need your help. I contacted feedbackassistant.apple.com/, but he didn’t help me.

I've shown that I can't use a certificate to decrypt my encrypted string, asking for help on what to use to decrypt it. As it turned out, I used the wrong certificate for signture decryption.

Hello Ray,

I’m not a security specialist so I’m not sure how to answer your question. IT’s good that you’ve submitted the documentation enhancement request. My current investigation is to see if I can learn of a process using commandline tools to validate the signature. If I can do this, then I’ll explain the technique and you would then be able to program this to your server. This is where my efforts are now.

I’m not a security expert, so I’m not sure how to answer your question. You have submitted a document enhancement request, which is good. My current investigation is to see if I can understand the process of validating signatures using a command line tool. If I can do that, I’ll explain the technique, and then you can program it to your server. That’s what I’m working on now.

Hello Ray,

I’ve been provided an RFC on which we based our JSON Web Signature implmentation. It’s another reference which you can make use of pending the release of documentation from the App Store Server team.

I have acquired an RFC on which our JSON Web signature implementation is based. This is another reference that you can use while waiting for the App Store Server team to release the documentation.

RFC 7515

Sincerely Yours,

RFC 7515 reads:

4.1.6. “x5c” (X.509 Certificate Chain) Header Parameter

The “x5c” (X.509 certificate chain) Header Parameter contains the X.509 public key certificate or certificate chain RFC5280 corresponding to the key used to digitally sign the JWS. The certificate or certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of RFC4648 — not Base64url-encoded) DER ITu.x690.2008 PKIX certificate value. The certificate containing The public key corresponding to the key used to digitally sign the JWS MUST be the first certificate. This MAY be followed by additional certificates, with each subsequent certificate being the one used to certify the previous one. The recipient MUST validate the certificate chain according to RFC5280 and consider the certificate or certificate chain to be invalid if any validation failure occurs.

The “X5C” (X.509 Certificate chain) header parameter contains the X.509 public key certificate or certificate chain RFC5280, corresponding to the key used to digitally sign the JWS. A certificate or certificate chain is represented as a JSON array of certificate value strings. Each string in the array is the DER ITU-X690.2008 PKIX certificate value obtained by base64 encoding (section 4 of RFC4648 — not Base64URL encoding). The certificate containing the public key corresponding to the key used to digitally sign the JWS must be the first certificate. This can be followed by additional certificates, each subsequent certificate being used to validate the previous certificate. The receiving party must verify the certificate chain in accordance with RFC5280 and if any validation failure occurs, the certificate or certificate chain is considered invalid. The use of this heading parameter is optional.

  • From the above, we know how to verify the certificate chain, which is verified in this way RFC5280.
  • For those who are not familiar with X.509 certificates, you can visit the basic principles and applications of X.509 digital certificates

Here is my source code for parsing JWS strings using PHP


                       / / php8.0 environment
use Firebase\JWT\JWT; // Find a library on jwt. IO that can be used quickly
use Firebase\JWT\Key; //composer require firebase/php-jwt
require __DIR__ . '/vendor/autoload.php';
error_reporting(E_ALL ^ E_WARNING ^ E_NOTICE);
$components = explode('. '.$jws);
$header = base64_decode($components[0]);
$payload = base64_decode($components[1]);
$signature = base64_decode($components[2]);

$headerJson = json_decode($header.true);
$algorithm = $headerJson['alg'];
$x5cArray = $headerJson['x5c'];
$certificate = '-----BEGIN CERTIFICATE-----' . PHP_EOL;
$certificate .= chunk_split($headerJson['x5c'] [0].64, PHP_EOL);
$certificate. ='-----END CERTIFICATE-----' . PHP_EOL;
$pkey_object = openssl_pkey_get_public($certificate);
$pkey_array = openssl_pkey_get_details($pkey_object);
$publicKey = $pkey_array['key'];
// Pass in the JWS with the public key and encryption algorithm
$decoded = JWT::decode($jws.new Key($publicKey.$algorithm));
Copy the code

Here we mainly do the following things:

  1. Gets three strings in JWS
  2. Json serialization of Header to obtain its algorithm and x5C certificate chain.
  3. Obtain the first parameter of the X5C certificate array and generate the public key certificate through the openssl command.
  4. The JWS, ALG, and public key are passed into Firebase’s JWT library for parsing.

Here is the end of decryption, but there are the following points more confused:

  • Can the public key we obtain from X5C be true and not tampered with? The above code is not guaranteed.
  • Since the public key cannot be trusted, neither can the decrypted data.
  • Intercepts your data, modifies your data, encrypts the tampered data with the middleman private key, regenerates a new public key array and sends it to you.
  • Following current procedures, you will believe that this is a genuine signed message.

Above, we need to verify the authenticity of the public key, decrypt the data with the verified public key, and compare it with the decoded Payload or the client data. In the text above the code I mentioned:

  • From the above, we know how to verify the certificate chain, which is verified in this way RFC5280.
  • For those who are not familiar with X.509 certificates, you can visit the basic principles and applications of X.509 digital certificates.

This may be the final step in verifying the signature on the receipt;

Without further ado, on the code:


                        // PHP version >= 7.4
use Firebase\JWT\JWT;
use Firebase\JWT\Key; //composer require firebase/php-jwt
require __DIR__ . '/vendor/autoload.php';
error_reporting(E_ALL ^ E_WARNING ^ E_NOTICE);
$jws = 'eyJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlFTURDQ0E3YWdBd0lCQWdJUWFQb1BsZHZwU29FSDBsQnJqRFB2OWpBS0JnZ3Foa2pPUFFRREF6QjFNVVF3UWdZRFZRUURERHRCY0hCc1pTQlhiM0pzWkhkcFpHVWdSR1YyWld4dmNHVnlJRkpsYkdGMGFXOXVjeUJEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURUxNQWtHQTFVRUN3d0NSell4RXpBUkJnTlZCQW9NQ2tGd2NHeGxJRWx1WXk0eEN6QUpCZ05WQkFZVEFsVlRNQjRYRFRJeE1EZ3lOVEF5TlRBek5Gb1hEVEl6TURreU5EQXlOVEF6TTFvd2daSXhRREErQmdOVkJBTU1OMUJ5YjJRZ1JVTkRJRTFoWXlCQmNIQWdVM1J2Y21VZ1lXNWtJR2xVZFc1bGN5QlRkRzl5WlNCU1pXTmxhWEIwSUZOcFoyNXBibWN4TERBcUJnTlZCQXNNSTBGd2NHeGxJRmR2Y214a2QybGtaU0JFWlhabGJHOXdaWElnVW1Wc1lYUnBiMjV6TVJNd0VRWURWUVFLREFwQmNIQnNaU0JKYm1NdU1Rc3dDUVlEVlFRR0V3SlZVekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCT29UY2FQY3BlaXBOTDllUTA2dEN1N3BVY3dkQ1hkTjh2R3FhVWpkNThaOHRMeGlVQzBkQmVBK2V1TVlnZ2gxLzVpQWsrRk14VUZtQTJhMXI0YUNaOFNqZ2dJSU1JSUNCREFNQmdOVkhSTUJBZjhFQWpBQU1COEdBMVVkSXdRWU1CYUFGRDh2bENOUjAxREptaWc5N2JCODVjK2xrR0taTUhBR0NDc0dBUVVGQndFQkJHUXdZakF0QmdnckJnRUZCUWN3QW9ZaGFIUjBjRG92TDJObGNuUnpMbUZ3Y0d4bExtTnZiUzkzZDJSeVp6WXVaR1Z5TURFR0NDc0dBUVVGQnpBQmhpVm9kSFJ3T2k4dmIyTnpjQzVoY0hCc1pTNWpiMjB2YjJOemNEQXpMWGQzWkhKbk5qQXlNSUlCSGdZRFZSMGdCSUlCRlRDQ0FSRXdnZ0VOQmdvcWhraUc5Mk5rQlFZQk1JSCtNSUhEQmdnckJnRUZCUWNDQWpDQnRneUJzMUpsYkdsaGJtTmxJRzl1SUhSb2FYTWdZMlZ5ZEdsbWFXTmhkR1VnWW5rZ1lXNTVJSEJoY25SNUlHRnpjM1Z0WlhNZ1lXTmpaWEIwWVc1alpTQnZaaUIwYUdVZ2RHaGxiaUJoY0hCc2FXTmhZbXhsSUhOMFlXNWtZWEprSUhSbGNtMXpJR0Z1WkNCamIyNWthWFJwYjI1eklHOW1JSFZ6WlN3Z1kyVnlkR2xtYVdOaGRHVWdjRzlzYVdONUlHRnVaQ0JqWlhKMGFXWnBZMkYwYVc5dUlIQnlZV04wYVdObElITjBZWFJsYldWdWRITXVNRFlHQ0NzR0FRVUZCd0lCRmlwb2RIUndPaTh2ZDNkM0xtRndjR3hsTG1OdmJTOWpaWEowYVdacFkyRjBaV0YxZEdodmNtbDBlUzh3SFFZRFZSME9CQllFRkNPQ21NQnEvLzFMNWltdlZtcVgxb0NZZXFyTU1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3Tm9BREJsQWpFQWw0SkI5R0pIaXhQMm51aWJ5VTFrM3dyaTVwc0dJeFBNRTA1c0ZLcTdoUXV6dmJleUJ1ODJGb3p6eG1ienBvZ29BakJMU0ZsMGRaV0lZbDJlalBWK0RpNWZCbktQdThteW1CUXRvRS9IMmJFUzBxQXM4Yk51ZVUzQ0JqamgxbHduRHNJPSIsIk1JSURGakNDQXB5Z0F3SUJBZ0lVSXNHaFJ3cDBjMm52VTRZU3ljYWZQVGp6Yk5jd0NnWUlLb1pJemowRUF3TXdaekViTUJrR0ExVUVBd3dTUVhCd2JHVWdVbTl2ZENCRFFTQXRJRWN6TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd0hoY05NakV3TXpFM01qQXpOekV3V2hjTk16WXdNekU1TURBd01EQXdXakIxTVVRd1FnWURWUVFERER0QmNIQnNaU0JYYjNKc1pIZHBaR1VnUkdWMlpXeHZjR1Z5SUZKbGJHRjBhVzl1Y3lCRFpYSjBhV1pwWTJGMGFXOXVJRUYxZEdodmNtbDBlVEVMTUFrR0ExVUVDd3dDUnpZeEV6QVJCZ05WQkFvTUNrRndjR3hsSUVsdVl5NHhDekFKQmdOVkJBWVRBbFZUTUhZd0VBWUhLb1pJemowQ0FRWUZLNEVFQUNJRFlnQUVic1FLQzk0UHJsV21aWG5YZ3R4emRWSkw4VDBTR1luZ0RSR3BuZ24zTjZQVDhKTUViN0ZEaTRiQm1QaENuWjMvc3E2UEYvY0djS1hXc0w1dk90ZVJoeUo0NXgzQVNQN2NPQithYW85MGZjcHhTdi9FWkZibmlBYk5nWkdoSWhwSW80SDZNSUgzTUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDQVFBd0h3WURWUjBqQkJnd0ZvQVV1N0Rlb1ZnemlKcWtpcG5ldnIzcnI5ckxKS3N3UmdZSUt3WUJCUVVIQVFFRU9qQTRNRFlHQ0NzR0FRVUZCekFCaGlwb2RIUndPaTh2YjJOemNDNWhjSEJzWlM1amIyMHZiMk56Y0RBekxXRndjR3hsY205dmRHTmhaek13TndZRFZSMGZCREF3TGpBc29DcWdLSVltYUhSMGNEb3ZMMk55YkM1aGNIQnNaUzVqYjIwdllYQndiR1Z5YjI5MFkyRm5NeTVqY213d0hRWURWUjBPQkJZRUZEOHZsQ05SMDFESm1pZzk3YkI4NWMrbGtHS1pNQTRHQTFVZER3RUIvd1FFQXdJQkJqQVFCZ29xaGtpRzkyTmtCZ0lCQkFJRkFEQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqQkFYaFNxNUl5S29nTUNQdHc0OTBCYUI2NzdDYUVHSlh1ZlFCL0VxWkdkNkNTamlDdE9udU1UYlhWWG14eGN4ZmtDTVFEVFNQeGFyWlh2TnJreFUzVGtVTUkzM3l6dkZWVlJUNHd4V0pDOTk0T3NkY1o0K1JHTnNZRHlSNWdtZHIwbkRHZz0iLCJNSUlDUXpDQ0FjbWdBd0lCQWdJSUxjWDhpTkxGUzVVd0NnWUlLb1pJemowRUF3TXdaekViTUJrR0ExVUVBd3dTUVhCd2JHVWdVbTl2ZENCRFFTQXRJRWN6TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd0hoY05NVFF3TkRNd01UZ3hPVEEyV2hjTk16a3dORE13TVRneE9UQTJXakJuTVJzd0dRWURWUVFEREJKQmNIQnNaU0JTYjI5MElFTkJJQzBnUnpNeEpqQWtCZ05WQkFzTUhVRndjR3hsSUVObGNuUnBabWxqWVhScGIyNGdRWFYwYUc5eWFYUjVNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVFzd0NRWURWUVFHRXdKVlV6QjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkpqcEx6MUFjcVR0a3lKeWdSTWMzUkNWOGNXalRuSGNGQmJaRHVXbUJTcDNaSHRmVGpqVHV4eEV0WC8xSDdZeVlsM0o2WVJiVHpCUEVWb0EvVmhZREtYMUR5eE5CMGNUZGRxWGw1ZHZNVnp0SzUxN0lEdll1VlRaWHBta09sRUtNYU5DTUVBd0hRWURWUjBPQkJZRUZMdXczcUZZTTRpYXBJcVozcjY5NjYvYXl5U3JNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEZ1lEVlIwUEFRSC9CQVFEQWdFR01Bb0dDQ3FHU000OUJBTURBMmdBTUdVQ01RQ0Q2Y0hFRmw0YVhUUVkyZTN2OUd3T0FFWkx1Tit5UmhIRkQvM21lb3locG12T3dnUFVuUFdUeG5TNGF0K3FJeFVDTUcxbWloREsxQTNVVDgyTlF6NjBpbU9sTTI3amJkb1h0MlFmeUZNbStZaGlkRGtMRjF2TFVhZ002QmdENTZLeUtBPT0iXX0.eyJ0cmFuc2FjdGlvbklkIjoiMTAwMDAwMDkxNjkyMjk0MiIsIm9yaWdpbmFsVHJhbnNhY3Rpb25JZCI6IjEwMDAwMDA5MTY5MjI5NDIiLCJidW5kbGVJZCI6ImNvbS5qcC5oaW1lLmlvcyIsInByb2R1Y3RJZCI6ImtpbmcudGVzdC5nb2xkLjYwIiwicHVyY2hhc2VEYXRlIjoxNjM3NzIzODE2ODA5LCJvcmlnaW5hbFB1cmNoYXNlRGF0ZSI6MTYzNzcyMzgxNjgwOSwicXVhbnRpdHkiOjEsInR5cGUiOiJDb25zdW1hYmxlIiwiZGV2aWNlVmVyaWZpY2F0aW9uIjoicVZoOUYrOWVHZjlLUWgrQjNmSUFvUUtMOEt6MENrbVZHZlVpd3BQcmZjR2RsSnlodDc3NUlEOXl0U1FDV0l0eCIsImRldmljZVZlcmlmaWNhdGlvbk5vbmNlIjoiYTg3MzViY2YtODI1Zi00YWViLWI5OWMtNmY4NjZjYWRjOTZlIiwiYXBwQWNjb3VudFRva2VuIjoiMzk3NzExZDYtNjFiOC1iZmI3LWM5NGYtOGVhNjcwZmRiN2I3IiwiaW5BcHBPd25lcnNoaXBUeXBlIjoiUFVSQ0hBU0VEIiwic2lnbmVkRGF0ZSI6MTYzNzcyMzgxNjkxNH0.-ewQD6FbwdY_ycMHISNY7rp6VesmoJH_IURsX18JAVbb49CqUnjXHzxMHwTv_Pgs59DUIsUY1rt8cQWLWbDXMg';
$components = explode('. '.$jws);
$header = base64_decode($components[0]);
$payload = base64_decode($components[1]);
$signature = base64_decode($components[2]);

$headerJson = json_decode($header.true);
$algorithm = $headerJson['alg'];
$x5cArray = $headerJson['x5c'];

foreach ($x5cArray as $X5C) {
    $certificate = '-----BEGIN CERTIFICATE-----' . PHP_EOL;
    $certificate .= chunk_split($X5C.64, PHP_EOL);
    $certificate. ='-----END CERTIFICATE-----' . PHP_EOL;
    $certificates[] = openssl_x509_read($certificate);//OpenSSLCertificate
}
// Verify the validity of the certificate chain (each one is signed by the next one, except the root certificate), in reverse order, starting from the last one in the chain:
$applePemString = file_get_contents('/Users/Ray/PhpstormProjects/jws/AppleRootCA-G3.pem');
$applePem = openssl_x509_read($applePemString);
/ * * *@param OpenSSLCertificate|string|resource $certificate
    * @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key
    * @return int Returns 1 if the signature is correct, 0 if it is incorrect, and -1 on error.
 */
// First verify the last certificate in the X5c chain with the root certificate
$nextCode = openssl_x509_verify($certificates[0].$certificates[1]);
//Returns 1 if the signature is correct, 0 if it is incorrect, and -1 on error.
printf("The verification result of the first certificate to the second certificate is: %s\n".$nextCode);
if ($nextCode= =1) {
    $finalCode = openssl_x509_verify($certificates[1].$certificates[2]);
    printf("Second certificate to third certificate verification result: %s\n".$finalCode);
    // If the validation is correct, the penultimate certificate is validated with the first certificate
    if ($finalCode= =1) {
        $code = openssl_x509_verify($certificates[2].$applePem);
        printf("Root verification result: %s\n".$code);
        if ($code= =1) {
            // The first certificate is the one that signs the JWS
            $pkey_object = openssl_pkey_get_public($certificates[0]);
            $pkey_array = openssl_pkey_get_details($pkey_object);
            $publicKey = $pkey_array['key'];
            // Pass in the JWS with the public key and encryption algorithm
            $decoded = JWT::decode($jws.new Key($publicKey.$algorithm));
            // serialize the decrypted parameters
            $decoded_array = (array) $decoded;
            echo "Decrypted parameter :\n" . print_r($decoded_array.true)."\n"; }}}/* The verification result of the first certificate to the second certificate is as follows: 1 The verification result of the second certificate to the third certificate is as follows: 1 The root verification result is as follows: 1 Decrypted parameters: Array ( [transactionId] => 1000000916922942 [originalTransactionId] => 1000000916922942 [bundleId] => com.jp.hime.ios [productId] => king.test.gold.60 [purchaseDate] => 1637723816809 [originalPurchaseDate] => 1637723816809 [quantity] => 1  [type] => Consumable [deviceVerification] => qVh9F+9eGf9KQh+B3fIAoQKL8Kz0CkmVGfUiwpPrfcGdlJyht775ID9ytSQCWItx [deviceVerificationNonce] => a8735bcf-825f-4aeb-b99c-6f866cadc96e [appAccountToken] => 397711d6-61b8-bfb7-c94f-8ea670fdb7b7 [inAppOwnershipType] => PURCHASED [signedDate] => 1637723816914 ) */
Copy the code

What fields do I validate in X509 to prove that certificates are valid?

G3 certificate provided by Apple PKI

Thanks for watching.

The following is only recorded, not interpreted and followed up

Hello Rich,

I am happy to share a good news, I decrypted the JWS string as I wish, but it is so simple, it seems that it has nothing to do with the X.509 certificate chain. The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate. I benefit from this sentence, but This MAY be followed by additional certificates, with each subsequent certificate being the one used to certify the previous one. This sentence makes me very puzzled. I used PHP to verify the signature. I now compare the decrypted parameters with the parameters I sent to the server to verify the validity of the receipt. I don’t know if any better way to verify, please let me know if you have a better way. Below is my code, I hope you can help others.

Hello Ray,

Thank you very much for the script sample. If you don’t mind, I will anonymize the strings that are specific to you and your company. Than do I have your permission to distribute the script when asked – as a sample?