在处理Apple的服务器到服务器通知时解码X5C证书链

发布于 2025-02-11 04:32:48 字数 1721 浏览 1 评论 0 原文

我正在尝试从Apple的App Store处理服务器到服务器通知。以下注释详细说明了我发现的内容,我欢迎任何需要的更正。

该通知是在JSON数据包中收到的,可以从中提取“签名的PAYLOAD”。签名载荷是JWT的形式,因此可以分为标题,有效载荷和签名。

当标题为base64时,将导致另一个包含两个字段的JSON数据包:“ ALG”和“ X5C”。算法字段的值ES256,X5C字段包含由逗号分隔的三个证书的链。

有效载荷可以进行基础64解码,然后根据此处的信息解释: https://developer.apple.com/documentation/appstoreservernotifications/responsebodyv2decodedpayload

That part is简单的。但是,在验证签名之前,不应将有效载荷措施。

该签名将在此过程中保留。

返回证书链。链中的最后证书(证书3)是苹果的根证书Applerootca-G3证书。可以从 https://www.apple.com/certificateauthority/ CER文件可以使用OPENSL转换为PEM文件,如下所示:

openssl x509- inform der -in applerootca -g3.cer -outform -Outform -out pem -out applerootca -g3.pem

将PEM文件与证书进行比较3链中的3将确认它们是相同的。

同样,链中的中间证书(证书2)是苹果的中间证书Applewwdrcag6.cer。将其转换为PEM文件将确认它们是相同的。

链中的第一个证书(证书1)是实体证书。

可以使用OpenSSL将根和中间证书转换为文本,如下所示:

openssl x509 -text -in applerootca -g3.pem

openssl x509 -text -in applewwwdrcag6.pem

pem 将揭示每个证书的发行人和主题。 我知道,要使证书链有效,根的主题必须与中间人的发行人匹配,中间体的主题必须与实体的发行人匹配,并且根的主题和发行者必须相同。 https://docs.apigee.com/指南/验证 - 认证链#SplitCertchain

是这种情况。到目前为止,一切都很好。

从这一点开始,我不确定该怎么办。我的想法是,我需要完成证书链的验证,然后以某种方式计算一个签名,该签名需要匹配签名的签名,该签名是签名有效负载的一部分。这应该如何完成?我也不清楚我是否需​​要Apple开发人员帐户中的共享秘密,如果是这样,它如何使用?编码算法ES256也必须在某个地方使用,但我再次不确定在哪里或如何。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

香橙ぽ 2025-02-18 04:32:48

Apple的帖子包含一个“签名的销售负载”,即JWT - 因此,一旦您充分说服它确实来自Apple,就可以验证JWT签名并提取有效载荷。

所以如果我使用错误的术语等,请检查我的问题。

  1. (免责声明:我是一个加密新手, 这些在“ X5C”属性中列为base64编码的ASN1字节字符串。
  2. 然后,您要验证a)链条有效,b)链条以您可以信任的知名钥匙开始。 App Store Certs均由Apple的Root&您已经拥有的中间密钥。这些应该是“ X5C”中列出的链条中的最后两个证书。
  3. 这样做后,您可以通过将经过验证的签名密钥传递给您的方便的Dandy JWT解码库来解码JWT,该库将根据标题中的验证键验证其签名。

在Python中,这有点棘手**,因为Apple使用了混淆Pyjwt的格式和算法的某种组合。这是要点:

**委婉语

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

甜心 2025-02-18 04:32:48

**不是加密专家**
我挣扎了一段时间,因为苹果的文档在如何验证签名令牌方面非常稀少。

this 苹果开发人员视频很有帮助(有关验证签名的令牌启动〜11mm标记)。

我相信泰勒的整体步骤是准确的。这是我最终使用jsonwebtoken和crypto在节点中实现的示例代码:

const jwt = require('jsonwebtoken')
const fs = require('fs')
const crypto = require('crypto')

function decodePayload(token){
  // G3 Cert here https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
  var apple_G3_cert = fs.readFileSync('./AppleRootCA-G3.cer') // TODO: download cert from apple
  var x509G3 = new crypto.X509Certificate(apple_G3_cert)

  // Decode the token to obtain the headers
  var contents = jwt.decode(token,{complete: true})

  // Create certs from the x5c array
  var leaf_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[0],'base64'));
  var int_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[1],'base64'));
  var root_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[2],'base64'));

  // Validate the certs' issuers
  if(leaf_cert.checkIssued(int_cert) && 
    int_cert.checkIssued(root_cert) &&
    root_cert.checkIssued(x509G3)){

      // Chain is trusted - Verify signature
      try{
        return jwt.verify(token,leaf_cert.publicKey, {algorithms:'ES256'})
      }
      catch{
        return null
      }
  }else{
    return null
  }
}

** NOT A CRYPTO EXPERT **
I struggled through this for a while because Apple's docs are pretty sparse on how to validate the signed token.

This Apple Developer video was helpful (information regarding verifying signed tokens starts ~11m mark).

I believe Taylor's overall steps are accurate. Here is sample code of what I ended up implementing using jsonwebtoken and crypto in node:

const jwt = require('jsonwebtoken')
const fs = require('fs')
const crypto = require('crypto')

function decodePayload(token){
  // G3 Cert here https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
  var apple_G3_cert = fs.readFileSync('./AppleRootCA-G3.cer') // TODO: download cert from apple
  var x509G3 = new crypto.X509Certificate(apple_G3_cert)

  // Decode the token to obtain the headers
  var contents = jwt.decode(token,{complete: true})

  // Create certs from the x5c array
  var leaf_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[0],'base64'));
  var int_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[1],'base64'));
  var root_cert = new crypto.X509Certificate(Buffer.from(contents.header.x5c[2],'base64'));

  // Validate the certs' issuers
  if(leaf_cert.checkIssued(int_cert) && 
    int_cert.checkIssued(root_cert) &&
    root_cert.checkIssued(x509G3)){

      // Chain is trusted - Verify signature
      try{
        return jwt.verify(token,leaf_cert.publicKey, {algorithms:'ES256'})
      }
      catch{
        return null
      }
  }else{
    return null
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文