In Forensic Detection of AI-Generated Images: A Practical Walkthrough I covered three complementary techniques for authenticating digital photographs, including C2PA content credentials. The C2PA section demonstrated how Pixel 8 Pro photos carry a cryptographically signed manifest binding the image bytes to the device that produced them, and how a verifier can confirm that binding with the open-source c2patool.

That post showed the happy path. This one is about the unhappy path: what happens when you point c2patool at a fresh Pixel 10 photo and the validator returns Invalid, what the standard remediation advice gets wrong, and how to find the correct trust anchor in about a minute by reading the cert chain itself.

If you do forensic work with C2PA-signed media, this is the difference between a finding of “we could not verify provenance” and “the device-bound signing chain validates cleanly.” The mechanics are simple once you see them. The trap is that the most obvious-sounding fix is for a completely different Google PKI.

The Setup

I had a Pixel 10 JPEG and a current build of c2patool:

$ c2patool --version
c2patool 0.26.50

$ c2patool ~/exchange/test.jpg

The tool returns a long JSON report. The interesting part is the validation summary at the bottom:

"failure": [
  {
    "code": "signingCredential.expired",
    "explanation": "certificate expired"
  },
  {
    "code": "signingCredential.untrusted",
    "explanation": "signing certificate untrusted"
  }
],
"informational": [
  {
    "code": "timeStamp.untrusted",
    "explanation": "timestamp cert untrusted: Google Pixel Time Stamping Authority"
  }
],
"validation_state": "Invalid"

Three problems, one verdict. The signing certificate is not in c2patool’s trust store, the certificate is past its notAfter, and the embedded timestamp authority is also not trusted. Any one of those alone would block a forensic “validates cleanly” finding.

The False Start

If you ask a popular large language model how to fix signingCredential.untrusted for a Pixel image, it will confidently suggest installing an Android Key Attestation root certificate. That is the trust anchor used to verify that a hardware-backed cryptographic key originated inside a Google-certified Android device. It carries a subject of CN=Key Attestation CA1, OU=Android, O=Google LLC. There was even a recent rotation, on 2026-04-10, that switched the Key Attestation hierarchy to ECDSA P-384, which makes the suggestion sound timely and specific.

I dutifully saved the recommended PEM, passed it as a trust anchor, and re-ran c2patool.

Same three failures. Nothing changed.

This is the moment where it is tempting to assume you have the right cert and try harder. Maybe the file is malformed. Maybe c2patool needs a settings file. Maybe Pixel images need a special validation flag. None of these are the issue.

The issue is that Android Key Attestation and C2PA signing are two different public-key hierarchies, both operated by Google, with two different roots. Attestation says, this key lives in real Pixel hardware. C2PA says, this manifest was signed by an authorized camera identity. They share branding, not chains. A trust anchor from the attestation PKI tells c2patool nothing about the validity of a C2PA signature.

You cannot tell from a cert subject alone whether two roots are in the same family or different families. You have to look at the actual chain in the asset.

Reading the Chain That Is Actually There

c2patool exposes a flag for exactly this:

c2patool --certs ~/exchange/test.jpg

That dumps the PEM-formatted certificate chain embedded in the C2PA manifest. Decoding the two issued certs with openssl x509 -noout -subject -issuer gives:

leaf:
  subject = CN=Pixel Camera, O=Google LLC, C=US
  issuer  = CN=Google C2PA Mobile A 1P ICA G3 L1, O=Google LLC, C=US

ICA:
  subject = CN=Google C2PA Mobile A 1P ICA G3 L1, O=Google LLC, C=US
  issuer  = CN=Google C2PA Root CA G3, O=Google LLC, C=US

That is the answer in one screen. The chain we need to anchor is Pixel Camera → Google C2PA Mobile A 1P ICA G3 L1 → Google C2PA Root CA G3. The Key Attestation CA1 root never appears in this chain because it is not part of C2PA signing. It is part of a parallel attestation hierarchy that lives on the device for a different purpose.

The ICA cert also conveniently includes an Authority Information Access (AIA) extension pointing at the root:

http://pki.goog/c2pa/root-g3.crt

That URL is the definitive source. Fetch it, convert from DER to PEM, and you have the right trust anchor:

mkdir -p ~/.config/c2patool
curl -fsSL http://pki.goog/c2pa/root-g3.crt -o /tmp/root-g3.der
openssl x509 -inform DER -in /tmp/root-g3.der -out ~/.config/c2patool/google_c2pa_root_g3.pem
openssl x509 -in ~/.config/c2patool/google_c2pa_root_g3.pem -noout -subject -dates

Output:

subject=C = US, O = Google LLC, CN = Google C2PA Root CA G3
notBefore=May  8 22:32:21 2025 GMT
notAfter=May  8 22:32:21 2050 GMT

This is the right root. A 25-year-lived ECDSA P-384 self-signed cert with a Google C2PA Root CA G3 subject, which is exactly what the ICA in our image was issued by.

Telling c2patool Where to Look

c2patool reads trust anchors from a file passed via the trust subcommand or through the C2PATOOL_TRUST_ANCHORS environment variable. Either works. The env var is more convenient because it survives across sessions and across the dozens of small one-off validations you tend to run during a casework day:

export C2PATOOL_TRUST_ANCHORS="$HOME/.config/c2patool/google_c2pa_root_g3.pem"

Add that line to ~/.bashrc (or your shell’s equivalent) and your default c2patool runs will pick it up automatically.

Now re-run validation:

$ c2patool ~/exchange/test.jpg trust | grep validation_state
"validation_state": "Trusted"

The full validation report now shows:

"success": [
  { "code": "timeStamp.validated" },
  { "code": "timeStamp.trusted",
    "explanation": "timestamp cert trusted: Google Pixel Time Stamping Authority" },
  { "code": "signingCredential.trusted",
    "explanation": "signing certificate trusted, found in System trust anchors" },
  { "code": "claimSignature.insideValidity",
    "explanation": "claim signature valid" },
  { "code": "claimSignature.validated" },
  ... (assertion hashes all match) ...
],
"failure": [],
"validation_state": "Trusted"

Three observations are worth pausing on.

First, the empty failure array. A single root cert closed all three findings. The signing chain is now anchored, the timestamp authority is now anchored, and the expiry-related failure also resolved.

Second, the appearance of claimSignature.insideValidity. This is how the C2PA validator handles the “expired” finding. The Pixel 10 leaf cert in this image had a 30-day validity window, and it had in fact expired four days before my testing. Under naive certificate validation, that should be fatal. C2PA, however, accepts a signature as long as the embedded timestamp authority can prove that the signing happened while the cert was still valid. The TSA was previously flagged as untrusted precisely because the same Google C2PA Root CA G3 anchors the timestamp chain. Once the root was trusted, the TSA became trusted, and the TSA in turn certified that the camera signed the manifest before the cert expired. That is the chain of reasoning the validator carries out, and it is the whole reason that short-lived signing certs are operationally safe in C2PA. A trusted timestamp transforms an expired cert into a still-valid historical signature.

Third, signingCredential.trusted says the cert was found in System trust anchors. That phrasing surprised me until I read the c2patool source. When you pass a custom trust anchor file, c2patool merges it with its built-in trust store and reports both under the same “System” label.

The Operational Recipe

For anyone doing forensic work on Pixel 10 or other Google C2PA-signed media, this is the entire checklist:

  1. Install a recent c2patool (>= 0.26.0).
  2. Save the root from http://pki.goog/c2pa/root-g3.crt to ~/.config/c2patool/google_c2pa_root_g3.pem after converting it from DER to PEM.
  3. Set C2PATOOL_TRUST_ANCHORS to that path in your shell profile.
  4. Validate with c2patool <image> trust.

That is it. No tinkering with Settings files, no Android attestation roots, no patching the validator. The validator already knows how to do its job; it just needs the right trust anchor.

Lessons That Generalize

Three takeaways are worth keeping for the next time C2PA validation fails on a vendor’s device.

Read the chain, do not guess the root. c2patool --certs is the fastest way to answer the question “what trust anchor do I need.” It tells you the exact issuer of the leaf and, via the ICA’s AIA extension, where to find the root. Both of those beat any documentation or AI-generated guidance, because they are inside the artifact itself. If you adopt one habit from this post, make it that one.

Brand is not chain. When Google operates more than one PKI under similar-sounding names (Key Attestation, C2PA Signing, plus the various TSA hierarchies), a subject string like “Google LLC” tells you nothing about which hierarchy a certificate participates in. Forensic verifications are about chains, not logos. The same warning applies to Apple, Microsoft, Adobe, and any vendor large enough to operate multiple internal CAs.

Expired does not mean invalid. C2PA’s reliance on Time Stamp Authorities to certify the historical validity of short-lived signing certs is one of the smartest decisions in the spec. It is also one of the most frequently misunderstood by people accustomed to TLS-style “is the cert currently valid” thinking. If you see signingCredential.expired together with timeStamp.untrusted, fix the timestamp trust and the expiry warning usually resolves on its own. If you see expired alone with a trusted TSA, that is a finding worth investigating, because it suggests the signature was applied after the cert expired.

C2PA is still young as a deployed standard, and the trust-store ergonomics will improve. Until then, the discipline that protects you is the same discipline that protects every digital forensics finding: verify the chain you actually have, not the chain a tool or a chatbot tells you should be there.

References