Back to documentation

Last updated: May 15, 2026 · Last reviewed against Apple docs: May 15, 2026

How Apple certificate chain verification works

Apple certificate chain verification is the X.509 chain validation that proves a JWS-signed App Store Server Notification V2 was issued by Apple. It pairs with signature verification to confirm two things about a payload: that the signing certificate is trusted, and that the bytes of this specific payload were signed by the matching private key. Both checks have to pass before a handler can trust the contents.

This doc covers what Apple ships in the signed payload, what each part of the chain proves, and how to pick the trust anchor a verifier uses. The rules apply whether you build the verifier yourself or use a service that does.

What Apple sends in signedPayload

In App Store Server Notifications V2, the request body is a JSON object with a single field, signedPayload, holding a compact JWS. The JWS comes apart at the dots into three base64url-encoded segments:

  • a header
  • a payload
  • a signature

The header has three fields a verifier cares about:

  • alg: must be ES256 (ECDSA over the P-256 curve with SHA-256). Apple does not currently use any other algorithm. A handler should reject anything else.
  • crit: a JOSE field that names header parameters the verifier must understand. Apple does not set this. A strict handler rejects any payload that includes it, since accepting unknown critical parameters lets an attacker tell the verifier to skip checks of their choosing.
  • x5c: an array of base64-encoded certificates. The first entry is the signing certificate (the leaf), and subsequent entries are intermediates that chain it back toward a trusted root.

The three roles in the chain

The certificates Apple ships in x5c play three distinct roles.

  • Leaf. The certificate whose private key signed the payload. Its public key is what the signature check uses. The leaf must carry Apple's receipt-signing extension OID 1.2.840.113635.100.6.11.1, which is how a verifier distinguishes a real Apple receipt signer from any other Apple-issued certificate.
  • Intermediate. Apple chains the leaf back to the Apple Worldwide Developer Relations (WWDR) intermediate, which carries the OID 1.2.840.113635.100.6.2.1. A verifier should confirm that at least one intermediate in the chain carries this OID before trusting it as Apple's WWDR.
  • Root. The trust anchor. A self-signed CA certificate that anchors the chain. Apple publishes its roots through Apple PKI. A verifier loads them into its trust store ahead of time and pins them by SHA-256 fingerprint.

The order in x5c runs leaf first, then intermediates, and optionally the root. A correct verifier ignores any root that arrived in the header. The trust anchor has to come from somewhere the verifier already trusts.

Chain validation vs. signature verification

Chain validation proves the signing certificate (the leaf) was issued by Apple, through Apple's WWDR intermediate, anchored at an Apple root the verifier trusts. It does not prove anything about the payload content.

Signature verification proves the bytes of this specific payload were signed by the private key matching the leaf's public key. It does not prove the leaf was issued by anyone in particular.

Together, the two checks prove the payload came from Apple. A signature alone does not say who signed it. A chain alone does not say what was signed.

A valid certificate chain does not replace signature verification. A valid signature does not replace chain verification. Apple webhooks require both.

Where the trust anchor comes from

The root must come from a copy of Apple's root the verifier already has, not from the x5c header that arrived with the request. This is where most chain implementations go wrong.

Apple publishes its root certificates through Apple PKI. A verifier loads them into its trust store at startup and pins them by SHA-256 fingerprint. The chain has to terminate at a root whose fingerprint matches one of those, or the chain fails.

On .NET, the relevant settings are X509ChainTrustMode.CustomRootTrust (only the loaded roots are trusted, regardless of the operating system's default trust store) and DisableCertificateDownloads = true (no runtime AIA fetches to fill in missing intermediates). Both together make the chain deterministic. The verifier validates what Apple shipped against the roots it loaded, with no live network involvement on the request path.

Common chain-verification mistakes

  • Trusting the root in x5c. The header is allowed to ship the root, but a verifier that lets the shipped root be the trust anchor has no chain at all. Anyone with their own root can sign anything. Pin Apple's roots locally and ignore whatever root rides in the header.
  • Accepting any JWS algorithm. Apple uses ES256. A handler that accepts whatever alg value arrives can be downgraded by an attacker swapping in a different algorithm. Pin alg to ES256.
  • Skipping the receipt-signing OID check. Apple issues certificates for many purposes. The receipt-signing extension OID 1.2.840.113635.100.6.11.1 is what marks a leaf as authorized to sign App Store Server Notifications. Without the check, any leaf Apple ever issued could sign a webhook.
  • Accepting crit headers. Apple does not set crit. A handler that accepts unknown critical headers can be told to skip checks the attacker chose.
  • Falling back to the OS trust store. The system trust store includes hundreds of CAs that are not Apple. A chain built against the system store can succeed for a payload Apple did not sign. Use CustomRootTrust (or the equivalent in your stack) and trust only the roots you loaded for this purpose.
  • Relying on AIA fetches for missing intermediates. A chain that requires a runtime network call to complete is a chain that can hang on a slow lookup, fail when the AIA URL is unreachable, or be downgraded if an attacker can influence the network. Disable certificate downloads and require Apple to ship the full chain in x5c.

Frequently asked questions

SubTru's Apple chain verifier enforces the rules above. Apple roots come from a local trust bundle, pinned by SHA-256 fingerprint. .NET's CustomRootTrust constrains trust to those roots only, certificate downloads are disabled so chain validation never depends on a runtime network fetch, and the JWS algorithm is fixed at ES256 with no crit header accepted. The leaf must carry Apple's receipt-signing OID, and at least one intermediate must carry the WWDR OID, before the verifier considers the payload trusted.

Sources