Decoding the x5c certificate chain while processing a server-to-server notification from Apple

Viewed 220

I am trying to process a server-to-server notification from Apple’s app store. The following notes detail what I have discovered, and I welcome any correction that is needed.

The notification is received in a JSON packet and the “signedPayload” can be extracted from it. The signedPayload is in the form of a JWT and can therefore be split into the Header, Payload and Signature.

When the Header is base64 decoded it results in another JSON packet which contains two fields: “alg” and “x5c”. The algorithm field has the value ES256 and the x5c field contains a chain of three certificates separated by commas.

The Payload can be base64 decoded and then interpreted according to the information here: https://developer.apple.com/documentation/appstoreservernotifications/responsebodyv2decodedpayload

That part is simple. However the payload should not be actioned until the signature is verified.

The Signature is kept for later in the process.

Back to the certificate chain. The last certificate in the chain (certificate 3) is Apple’s root certificate AppleRootCA-G3 certificate. This can be downloaded from https://www.apple.com/certificateauthority/ The cer file can be converted to a pem file using openssl as follows:

openssl x509 -inform der -in AppleRootCA-G3.cer -outform pem -out AppleRootCA-G3.pem

Comparing the pem file with certificate 3 in the chain will confirm that they are identical.

Similarly, the middle certificate in the chain (certificate 2) is Apple’s intermediate certificate AppleWWDRCAG6.cer. Converting it to a pem file will confirm they are identical.

The first certificate in the chain (certificate 1) is the entity certificate.

The root and intermediate certificates can be converted to text using openssl as follows:

openssl x509 -text -in AppleRootCA-G3.pem

openssl x509 -text -in AppleWWDRCAG6.pem

This will reveal the issuer and subject of each certificate. I understand that for the certificate chain to be valid, the subject of the root must match the issuer of the intermediate, the subject of the intermediate must match the issuer of the entity and the subject and issuer of the root must be the same. https://docs.apigee.com/how-to-guides/validating-certificate-chain#splitcertchain

This is the case. So far, so good.

I am not sure what to do from this point onwards. My thinking is that I need to complete the validation of the certificate chain and then somehow calculate a signature which needs to match the Signature which formed part of the signed payload. How should this be done? I am also unclear as to whether I need the shared secret from our Apple developer’s account, and if so how is it used? The encoding algorithm ES256 must also be used somewhere but again I'm not sure where or how.

1 Answers

The POST from Apple contains a "signedPayload", which is a JWT -- so the job is to validate the JWT signature and extract the payload once you're sufficiently convinced it's really from Apple.

(DISCLAIMER: I am a crypto novice so check me on this if I'm using the wrong terms, etc. throughout)

  1. The header of the JWT contains the certificate chain used to sign it, so first you extract these public keys. These are listed in the "x5c" attribute as base64-encoded asn1 byte strings.
  2. Then, you want to validate that a) the chain is valid, and b) the chain starts with a well-known key you can trust. The App Store certs are all signed by Apple's root & intermediate keys which you already have. These should be the last two certificates in the chain listed in "x5c".
  3. Once you do that, you can decode the JWT by passing the validated signing key to your handy dandy JWT decoding library, which will verify its signature based on the validated key in the header.

This was a little tricky** in python because Apple is using some combination of formats and algorithms that confuses PyJWT. Here's a gist: https://gist.github.com/taylorhughes/3968575b40dd97f851f35892931ebf3e

** euphemism

Related