C2PA で AI 画像に来歴を付けて読む — c2patool で「AI 生成」を書き込み、検証の結果を AI に説明させた

AI で作った画像に「AI で生成した」と機械が読める形で書き込む方法の一つに、C2PA(Coalition for Content Provenance and Authenticity)の来歴の記録がある。仕様は公開されていて、記録を書き込む道具も無償で手に入る。ところが、書き込んだ記録を読み戻すと、返ってくるのは Valid や signingCredential.untrusted といった状態とコードが並ぶ JSON だ。何が確かめられ、何が確かめられていないのかは、一目では分からない。
そこで、当サイトの記事画像(ComfyUI で生成した PNG)1 枚に、c2patool 0.28.4 と、c2patool に同梱された試験用の鍵で C2PA の記録を付けて読み戻した。あわせて、2026 年 1 月の記事画像に最初から付いていた、Google LLC の証明書で署名された記録も読んだ。さらに、c2patool が出した JSON を claude -p(Claude Sonnet 5.5)と、手元の GPU で動かした gemma4:12b に読ませ、JSON から作った正解で採点した。どれも各条件 1 回の実行である。なお、署名した画像は公開しておらず、この記事の画像にも C2PA は付けていない。
先に結果を書く。この実行では、試験用の鍵で署名した画像の検証の状態は Valid で、同時に「署名した証明書が信頼リストに無い」(signingCredential.untrusted)と出た。ファイルは 59,226 バイト(6.8%)増え、署名は直後の検証を含めて 0.09 秒で終わった。
一方、2026 年 1 月の画像は、証明書の期限切れ(signingCredential.expired)が加わって Invalid だった。AI は 2 つのモデルとも、「AI で生成したと記録されているか」と「誰が署名したか」に 6 回すべて正解した。差が出たのは「何が検証されていないか」の挙げ方で、記録の内容そのものが本当かどうかは検証の対象ではない、という点に触れたのは Claude だけだった。
AI で作った画像に機械が読める印を付ける話は、EU の AI 法の透明性の規則とも関わる。期限と枠組みは AI 生成の表示と「印」は画像と3Dデータのどこで消えるのか(2026-11-09 公開) と G検定の法律・倫理は AI で現行制度に更新してから覚える — AI 法・基本計画・著作権・EU AI Act の一次資料をシラバスに対応づける(2026-10-04 公開) にまとめた。本記事は法の解説ではない。作り手の手元で C2PA の記録を付けて読むと何が出るかを確かめる記事である。
※本記事にはアフィリエイト広告(PR)が含まれます。
- C2PA の来歴は、ファイルに埋め込んだ署名つきの「マニフェスト」として読む
- 「AI 生成」を示すのは c2pa.created の digitalSourceType で、AI 開示は別のアサーションである
- 検証の状態は仕様と c2patool で名前がずれる — Well-Formed・Valid・Trusted と信頼リスト
- 実測の条件 — 当サイトの ComfyUI の画像に、c2patool の試験用の鍵で署名した
- 結果 — Valid だが署名者は信頼リストに無く、ファイルは 59,226 バイト増えた
- 2026 年 1 月の記事画像にあった Google LLC の署名は Invalid だった
- c2patool の JSON を claude -p と gemma4:12b に読ませ、JSON から作った正解で採点した
- 読み方 — 検証が通っても「AI 生成」が証明されたわけではない
- この実測で言えないこと
- まとめ — C2PA の記録は、状態・コード・署名者・宣言を分けて読む
- 参照
C2PA の来歴は、ファイルに埋め込んだ署名つきの「マニフェスト」として読む

C2PA の記録は、画像ファイルの中に「マニフェスト」として埋め込まれる。マニフェストの中心は、そのファイルについての「主張」(claim)だ。主張は、個々の記述である「アサーション」(どう作ったか、どのモデルを使ったか、など)の並びと、主張を作ったソフトの名前(claim_generator_info)を含み、主張全体に X.509 の証明書で署名が付く。c2patool の既定の署名の方式は、ECDSA と SHA-256 の組み合わせ(ES256)である。
マニフェスト・アサーション・主張は、仕様の manifest・assertion・claim の訳で、この記事ではこの 3 語に揃える。仕様の題は「Content Credentials : C2PA Technical Specification」で、C2PA の記録は Content Credentials とも呼ばれる。現行の版は 2.5(2026 年 10 月)だ。仕様の文書は CC BY 4.0 で公開されているので、本記事で英文を引くときは出典を添える。
アサーションには、置き場所が 2 つある。仕様によると、created_assertions に入れたものはすべて署名者に帰属する(All created_assertions are attributed to the signer as the Trust Model is rooted in the trust of the signer.)。一方の gathered_assertions は、主張の一部ではあるが、主張を作ったソフトが出どころではなく、署名者にも帰属しない、と仕様は説明している。
c2patool の出力の JSON では、この区別が各アサーションの created のキーに出る。"created": true なら署名者に帰属する側(created)で、無ければ帰属しない側(gathered)だ。c2pa-rs(c2patool が使う Rust の SDK)の既定は false で、manifest の JSON に書き足したアサーションは、何も指定しなければ gathered になる。この違いは、後の実測と AI の採点の両方で効いてくる。
ファイルを読むと、c2patool は検証の結果を同じ JSON に書く。この記事で読むのは、次のキーである。
active_manifest: いま有効なマニフェストの名前。manifestsの中のどれを読むかを指すmanifests[…].assertions: アサーションの並び。各要素にlabel・data・createdが入るmanifests[…].signature_info: 署名の情報。alg・issuer・common_nameなどmanifests[…].claim_generator_info: 主張を作ったソフトの名前と版validation_state: 検証の状態(Invalid・Valid・Trusted のどれか)validation_status: 失敗のコードだけを並べた配列validation_results.activeManifest: 成功(success)・情報(informational)・失敗(failure)の 3 つの配列
来歴の署名は、生成 AI をめぐる偽の画像やなりすましへの備えの一つでもある。生成 AI のリスクと対策の全体像を教科書で押さえておくと、来歴の署名がどこを受け持つのかを位置づけやすい。
「AI 生成」を示すのは c2pa.created の digitalSourceType で、AI 開示は別のアサーションである

C2PA の記録のどこに「AI で生成した」と書くのか。答えは、c2pa.actions のアサーションの先頭にある c2pa.created の動作に付ける digitalSourceType の値だ。
仕様 2.5 の 16.4.1.2 は、新しく作った資産では、最初の c2pa.actions の動作の並びの先頭を c2pa.created にし、そこに digitalSourceType を記録すると定めている(For all assets, a corresponding digitalSourceType field, with an appropriate value, shall be recorded with the c2pa.created action, to indicate the nature of the asset at its inception.)。
ここで注意したいのは、c2pa.created そのものは AI 生成を意味しないことだ。仕様は c2pa.created を次のように説明する。
c2pa.created The asset was first created or generated. This action applies equally to assets created by humans, captured by a camera, generated by AI, produced by other means, or any combination thereof.
(出典: C2PA Technical Specification 2.5、CC BY 4.0)
つまり、人が描いた絵もカメラで撮った写真も、最初の動作は同じ c2pa.created になる。AI で生成したかどうかを分けるのは、そこに付く digitalSourceType の値である。値は、IPTC の語彙(NewsCodes の Digital Source Type)の用語か、仕様が定める C2PA 独自の値から選ぶ。IPTC の用語は cv.iptc.org/newscodes/digitalsourcetype/ で始まる http スキームの URI で記録される。ただし c2patool には、短い名前で渡せる。この記事で扱う IPTC の用語は次の 3 つだ。
| 値(短い名前) | IPTC の名前(en-GB) | IPTC の定義(en-GB) |
|---|---|---|
| trainedAlgorithmicMedia | Created using Generative AI | Digital media created algorithmically using an Artificial Intelligence model trained on captured content |
| compositeWithTrainedAlgorithmicMedia | Edited using Generative AI | Augmentation, correction or enhancement using a Generative AI model, such as with inpainting or outpainting operations |
| algorithmicMedia | Pure algorithmic media | Media created purely by an algorithm not based on any sampled training data, e.g. an image created by software using a mathematical formula |
(出典: IPTC NewsCodes「Digital Source Type」、CC BY 4.0)
本文で訳すと、trainedAlgorithmicMedia は「生成 AI で作った」もので、撮影などで集めた内容で学習した AI モデルが作ったメディアを指す。compositeWithTrainedAlgorithmicMedia は「生成 AI で編集した」もので、インペイントやアウトペイントのような、生成 AI による補正や加筆を指す。algorithmicMedia は学習データに基づかないアルゴリズムだけで作ったものだ。学習したモデルを使わないので、この記事の採点では AI 生成に数えない。
C2PA 独自の値は、c2pa.org/digitalsourcetype/ で始まる 2 つである。empty は、中身が実質的に空のメディア(白紙のキャンバスや長さ 0 の動画など)を表す。trainedAlgorithmicData は、集めた内容とデータから作ったモデルをアルゴリズムで使った結果のデータを表す。仕様は、結果が画像や動画のようなメディアではなく、CSV や pickle のようなデータ形式である点で trainedAlgorithmicMedia と違う、と説明している。
c2patool では、--create trainedAlgorithmicMedia を付けて署名すると、新規の作成として c2pa.created の動作が自動で付く。逆に --create も --update も付けないと、c2patool は「編集」として扱い、元のファイルを親の素材(ingredient)にして c2pa.opened の動作を足す。この場合、先頭の動作は c2pa.opened になる。AI で生成した画像を新しく記録するなら、--create を使う。
AI 開示(c2pa.ai-disclosure)は「どのモデルを使ったか」を書く
紛らわしいのが c2pa.ai-disclosure である。仕様の版の履歴によると、これは 2.4(2026 年 4 月)で追加された、機械が読める AI の透明性の情報のためのアサーションで、2.5 では 16.8.2 の節にある。この記事では「AI 開示」と呼ぶ。必須の項目は modelType だけで、値は仕様の表8(Model type values)から選ぶ。modelName(人が読めるモデル名)・modelIdentifier・contentProfile・scientificDomain・metadata(アサーションについての追加の情報)は任意だ。
contentProfile には、人の関与の度合い(humanOversightLevel)を fully_autonomous・prompt_guided・human_validated の 3 段階で書ける。仕様の 16.8.2.3 には、digitalSourceType との組み合わせの解釈が表で示されている。
| digitalSourceType | humanOversightLevel | 仕様の解釈 |
|---|---|---|
| trainedAlgorithmicMedia | fully_autonomous | 人の確認の無い、純粋な AI 生成(Pure AI generation, no human review) |
| trainedAlgorithmicMedia | prompt_guided | 人のプロンプトに導かれた AI 生成(AI generation guided by human prompts) |
| compositeWithTrainedAlgorithmicMedia | human_validated | AI による編集を、公開前に人の編集者が確認・承認したもの |
| digitalCreation(人が、生成を伴わない道具で作ったもの) | not applicable(当てはまらない) | 学習したモデルを使っていない。AI 開示のアサーションは付けない(No trained model invoked; AI Model Disclosure assertion is not attached) |
仕様は、AI 開示が既存の信号を置き換えるものではないと書く(The c2pa.ai-disclosure assertion is designed to complement existing C2PA signals rather than replace them.)。AI 生成を示すのはあくまで c2pa.created の digitalSourceType で、AI 開示はそこに「どのモデルで」を足す記述である。今回の実行では、AI 開示に modelType と modelName だけを書き、humanOversightLevel は書いていない。また modelType は、表8 に GGUF の形式を表す値が無いので、ほかのどの型にも当たらないモデルを表す c2pa.types.model にした。
それでは、AI 開示の中身は検証で確かめられるのか。c2pa-rs 0.91.7 の既知のアサーションのラベルの一覧に ai-disclosure は無く、仕様も検証する側に入力をスキーマで検査しないよう求めている(Validators should not validate inputs against these schemas.)。そのため、modelType が表8 の値かどうかを c2patool の検証が確かめる、と考える根拠は見当たらない。この実行でも、c2patool は書いた値をそのまま書き込み、読み戻しで同じ値を返した。
検証の状態は仕様と c2patool で名前がずれる — Well-Formed・Valid・Trusted と信頼リスト

C2PA の記録を読むときに最初に見るのは検証の状態だが、ここで仕様と c2patool の言葉がずれる。仕様は、状態を 3 段階の入れ子で定める(A C2PA Manifest may be one of the following: Well-Formed Valid Trusted Any Trusted manifest is also Valid, and any Valid manifest is also Well-Formed.)。
Valid の条件(13.3.5)は 5 つある。マニフェストが Well-Formed であること、署名の後にマニフェストが変更されていないこと、主張の署名が claimSignature.validated を得ること、署名の有効期間の検査が claimSignature.insideValidity を得ること、署名者の証明書が失効(signingCredential.ocsp.revoked)で拒否されていないことだ。Trusted の条件(13.3.6)は、Valid であることに加えて、署名した証明書が signingCredential.trusted を得ることである。
一方、c2patool が使う c2pa-rs の validation_state は Invalid・Valid・Trusted の 3 値で、Well-Formed に当たる値が無い。形は Well-Formed でも Valid の条件を満たさないものは、Invalid になる。さらに c2pa-rs は、失敗のコードが「署名した証明書が信頼リストに無い」(signingCredential.untrusted)だけなら、Valid として扱う。ところが仕様の付録 D では、signingCredential.untrusted は失敗のコードに分類されている。そのため、Valid の JSON の validation_status に失敗のコードが 1 つ並ぶ、という形になる。なお、c2pa-rs が Valid の判定で許容する失敗には、signingCredential.untrusted のほかに、CAWG(Creator Assertions Working Group)の身元のアサーションの失敗(cawg.x509. か cawg.identity. で始まるコード)もある。今回の 3 つの JSON に CAWG のコードは無い。
| 仕様の状態 | c2pa-rs の validation_state | 主な条件 |
|---|---|---|
| Well-Formed(だが Valid でない) | Invalid | Valid の条件を満たさない |
| Valid | Valid | Well-Formed・署名後に変更なし・claimSignature.validated・claimSignature.insideValidity・失効(signingCredential.ocsp.revoked)で拒否されていない。失敗が signingCredential.untrusted(と CAWG のコード)だけなら Valid |
| Trusted | Trusted | signingCredential.trusted |
validation_status には失敗のコードだけが入り、成功と情報のコードは validation_results.activeManifest の success と informational にある。したがって、validation_status だけを見ると、情報のコードとして出る注意を見落とす。
Valid の意味についても、仕様ははっきり書いている。Valid なら、主張は claim_generator_info で示される「主張を作ったソフト」に帰属できる(If a C2PA Manifest is Valid, then the manifest’s claim can be attributed to the claim generator which is identified by the claim_generator_info field of the claim)。署名者(証明書)と、主張を作ったソフトは別の項目である。この区別は、後で AI の答えを採点するときの基準にもなる。
信頼リストは道具ごとに違う
Trusted になるかどうかは、検証する側がどの信頼リストを持っているかで決まる。仕様の 13.4 は、検証する側の信頼アンカーに C2PA Trust List を含めるよう定め、利用者が追加の信頼アンカーを設定できるようにすべきだとも書く。C2PA の FAQ によると、C2PA Trust List は、生成の製品に署名用の証明書を出してよい認証局(CA)の一覧で、その認証局から証明書を受けられるのは、適合プログラム(conformance program)で審査され承認された製品だけである。
c2patool 側の既定はこうだ。c2pa-rs の設定は既定で信頼リストとの照合を行う(verify_trust: true)が、信頼アンカーの既定は空である(ソースを読んだ範囲)。一方、trust サブコマンドで信頼リストを指定すると、実行のたびにネットに取りに行く、と c2patool の文書は書く。今回の実行では、信頼リストも設定ファイルも渡していない。
ブラウザで確かめる道具もある。Adobe が主導する業界横断の団体 Content Authenticity Initiative(CAI)のオープンソースの文書(opensource.contentauthenticity.org)によれば、Content Credentials の確認サイト(Verify、verify.contentauthenticity.org)は暫定の信頼リスト(ITL)で照合し、ITL は 2026 年 1 月 1 日に凍結された。同じ文書によれば、Verify は知らない証明書で署名されたファイルに「The Content Credential issuer couldn’t be recognized. This file may not come from where it claims to.」という警告を出し、知っている証明書なら持ち主の名前と署名の時刻を出す。
また c2patool の文書によると、Adobe の Inspect は公式の C2PA Trust List を使う。道具ごとに信頼リストが違うので、同じファイルでも表示が違いうる。今回の画像は、Verify にも Inspect にも上げていない。
実測の条件 — 当サイトの ComfyUI の画像に、c2patool の試験用の鍵で署名した

道具は c2patool 0.28.4 の Windows 版である。GitHub の公式リリース(2026 年 10 月 26 日(UTC)公開、Latest)から配布物 v0.28.4-x86_64-pc-windows-msvc.zip を取得し、リリースに載っている SHA-256(00e5bcfef95fcf913cc3d67825d7d50ea1eb1180c7fdbef746c9ce3b65e272f1)と照合した。版の表示は「c2patool 0.28.4」で、内部では c2pa-rs の 0.91.7(crate 名は c2pa)を使う。ライセンスは MIT OR Apache-2.0 で、どちらかを選べる。
実行した PC は Intel Core Ultra 7 265F・RAM 63.7 GB・RTX 5070 Ti 16 GB の Windows 11 Pro だ。
入力は、2026-11-02 の記事のカバー画像(ComfyUI で生成した PNG、872,766 バイト)である。元のファイルは変えず、署名したファイルは別に書き出した。署名したファイルはどこにも上げていない。
署名には、c2patool に同梱の試験用の鍵と証明書(sample フォルダの es256_private.key と es256_certs.pem、ES256)を使った。c2patool は、署名の設定が何も無いときにこの証明書を組み込みのものとして使い、文書はそれを「This is only suitable for development.」(開発用に限る)と書いている。証明書の組織名は「C2PA Test Signing Cert」、名前(common name)は「C2PA Signer」、部署名は「FOR TESTING_ONLY」で、2030 年 8 月まで有効である。本物の署名用の証明書は買っていない。
この証明書で署名したファイルは、検証で「署名者が信頼リストに無い」(signingCredential.untrusted)と表示される。c2patool の文書も、見本の画像は試験用の証明書で署名しているため signingCredential.untrusted になる、と書いている。以下の結果は、この前提で読んでほしい。
条件は次の 3 つである。
| 条件 | 入力 | 署名 | AI 開示(c2pa.ai-disclosure) |
|---|---|---|---|
| AI 開示を created にした画像 | 2026-11-02 の記事のカバー画像 | --create trainedAlgorithmicMedia、試験用の鍵 | modelType と modelName を書き、"created": true を付けた |
| AI 開示を gathered にした画像 | 同じ | 同じ | 同じ内容で、created を書かなかった |
| 2026 年 1 月の画像 | 2026-01-13 の記事のカバー画像(587,246 バイト) | 署名しない(読むだけ) | — |
どちらの署名でも、タイムスタンプの発行元(ta_url)は指定していない。c2patool の見本の manifest の JSON(test.json)は DigiCert のタイムスタンプを指定しており、そのまま使うと署名のたびに外部へ接続する。今回は外部に接続しない形にしたので、署名の時刻を第三者は保証していない。仕様では、タイムスタンプは任意の補助の証拠である(a time-stamp in a C2PA Manifest is optional supplementary evidence)。
署名と読み戻しのコマンドは次のとおりだ。読み戻しは -d(詳細のレポート)を付けない既定の JSON で、AI に渡したのもこの JSON である。
c2patool <入力の PNG> -m <条件>_manifest.json --create trainedAlgorithmicMedia -o <出力の PNG> -f
c2patool <出力の PNG>
manifest の JSON は、AI 開示を created にした画像では次の内容にした(鍵と証明書のパスは置き換えた)。gathered の画像は、最後の "created": true が無いだけで、ほかは同じだ。
{
"alg": "es256",
"private_key": "<c2patool のフォルダ>/sample/es256_private.key",
"sign_cert": "<c2patool のフォルダ>/sample/es256_certs.pem",
"claim_generator_info": [{"name": "swiftwand-provenance-test", "version": "0.1"}],
"title": "cover.png",
"assertions": [
{
"label": "c2pa.ai-disclosure",
"data": {
"modelType": "c2pa.types.model",
"modelName": "Z-Image-Turbo (GGUF Q6_K) via ComfyUI"
},
"created": true
}
]
}
c2pa.actions は、manifest の JSON に書いていない。--create trainedAlgorithmicMedia が c2pa.created と digitalSourceType を付けるからだ。claim_generator_info の名前(swiftwand-provenance-test)は、この実験のために付けた名前である。
結果 — Valid だが署名者は信頼リストに無く、ファイルは 59,226 バイト増えた

| 条件 | 署名後の大きさ(バイト) | 署名の時間(秒) | validation_state | validation_status(失敗) | informational(情報) | issuer | common_name |
|---|---|---|---|---|---|---|---|
| AI 開示を created にした画像 | 931,992 | 0.09 | Valid | signingCredential.untrusted | signingCredential.ocsp.skipped | C2PA Test Signing Cert | C2PA Signer |
| AI 開示を gathered にした画像 | 931,992 | 0.093 | Valid | signingCredential.untrusted | signingCredential.ocsp.skipped | C2PA Test Signing Cert | C2PA Signer |
| 2026 年 1 月の画像 | —(署名していない) | — | Invalid | signingCredential.untrusted、signingCredential.expired | timeStamp.untrusted | Google LLC | Google Media Processing Services |
試験用の鍵で署名した 2 枚は、どちらも Valid だった。AI 開示を created にした画像では、success に claimSignature.insideValidity・claimSignature.validated・assertion.hashedURI.match(4 件)・assertion.dataHash.match が並び、failure は signingCredential.untrusted の 1 件だけだった。つまり、署名は検証でき、署名の有効期間内で、画像のデータのハッシュも記録と一致した。
そのうえで、署名した証明書が信頼リストに無い。c2pa-rs はこの失敗を Valid の判定で許容するので、状態は Valid で、Trusted にはならない。言い換えると、署名と改ざんの検査は通った(Valid)が、署名者が信頼リストに無いので Trusted ではない。「署名が無効」「検証に失敗した」という結果ではない。
読み戻した JSON の主な部分を抜き出すと、次のようになる(URL・ID・シリアル番号などは省いた)。
"assertions": [
{
"label": "c2pa.ai-disclosure",
"data": {
"modelName": "Z-Image-Turbo (GGUF Q6_K) via ComfyUI",
"modelType": "c2pa.types.model"
},
"created": true
},
{
"label": "c2pa.actions.v2",
"data": {
"actions": [
{
"action": "c2pa.created",
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
}
]
},
"created": true
}
],
"signature_info": {
"alg": "Es256",
"issuer": "C2PA Test Signing Cert",
"common_name": "C2PA Signer"
},
"validation_status": [
{"code": "signingCredential.untrusted", "explanation": "signing certificate untrusted"}
],
"validation_state": "Valid"
--create が付けた動作は、ラベル c2pa.actions.v2 のアサーションに入っていた。先頭が c2pa.created で、digitalSourceType は trainedAlgorithmicMedia の完全な URI である。こちらは両条件とも "created": true で、署名者に帰属する側にある。
一方、AI 開示は、created を書いた画像では "created": true で、書かなかった画像では created のキーそのものが無く、gathered として扱われた。検証の状態はどちらも Valid で、この違いでは変わらなかった。なお、3 つの JSON の validation_results.specVersion はどれも 2.4.0 で、C2PA の仕様 2.5 とは版の数字が違う。
ファイルは 6.8% 増え、署名は 0.09 秒だった
ファイルの大きさは、872,766 バイトから 931,992 バイトになった。差は 931,992 − 872,766 = 59,226 バイトで、元の大きさに対する割合は 59,226 ÷ 872,766 ≒ 6.8% である。AI 開示の created の有無で、大きさは変わらなかった。
読み戻した JSON には thumbnail(c2pa.thumbnail.claim)があり、形式は image/jpeg だった。増えた分には、このサムネイルが含まれる。c2pa-rs の既定ではサムネイルを作り、長辺は 1024、アルファの無い PNG からは JPEG のサムネイルを作る。したがって、記録を付けたファイルが大きくなる理由は、署名とアサーションだけではない。
署名の時間は、壁時計で 0.09 秒と 0.093 秒だった。c2patool は既定で署名の直後に検証するので(--no_signing_verify で省ける)、この時間には検証も含まれる。また、タイムスタンプを付けていないので、ネットの往復は含まない。どちらも 1 枚・各 1 回の値で、ほかの画像や PC での値ではない。
2026 年 1 月の記事画像にあった Google LLC の署名は Invalid だった

3 つ目の条件は、2026-01-13 の記事のカバー画像だ。当サイトが ComfyUI で記事画像を作るようになる前の画像で、手元のファイルに Google LLC の証明書で署名された C2PA の記録が付いていた。主張を作ったソフトは Google C2PA Core Generator Library、先頭の動作は c2pa.created で、digitalSourceType は trainedAlgorithmicMedia である。サムネイルのアサーションは無かった。
検証の状態は Invalid だった。failure は signingCredential.untrusted と signingCredential.expired(証明書の期限切れ。仕様の付録 D には無く、c2pa-rs が定義したコード)の 2 つである。先に書いたとおり、c2pa-rs が Valid の判定で許容する失敗は signingCredential.untrusted と CAWG のコードで、signingCredential.expired は含まれない。そのため、期限切れが加わったこのファイルは Invalid になる。一方で success には claimSignature.validated・claimSignature.insideValidity・assertion.dataHash.match があり、署名そのものは検証でき、画像のデータのハッシュも記録と一致していた。
タイムスタンプについては、success に timeStamp.validated が、informational に timeStamp.untrusted があった。タイムスタンプの中身は一致したが、その発行元がこの実行の信頼リストに無い、という意味である。試験用の鍵の 2 枚とは違い、こちらの署名にはタイムスタンプが付いている。
期限切れと Invalid は、この実行で c2patool がそう判定したという意味で、画像が偽物だという意味ではない。また、信頼リストを何も渡さずに読んだので、Google LLC の証明書も untrusted と判定された。Verify や Inspect のように別の信頼リストを持つ道具で読めば、表示は違いうる。証明書の期限切れの判定が署名の時刻とどう関わるかは、この JSON の項目だけでは確かめていない。
当サイトの記事画像に付いていた記録が、公開までのどの段で残り、どこで消えるかは AI 生成の表示と「印」は画像と3Dデータのどこで消えるのか(2026-11-09 公開) で数えた。本記事では、手元のファイルの記録を読んだ結果だけを扱う。
c2patool の JSON を claude -p と gemma4:12b に読ませ、JSON から作った正解で採点した

渡したものと問い
AI には、c2patool が出した既定の JSON の全文と次の問いを渡し、システムプロンプトは 2 つのモデルとも「You are a careful analyst of C2PA manifests.」の 1 文にした。c2patool が標準エラーに出すもの(組み込みの鍵を使ったときの警告など)は JSON に入らず、AI には渡していない。この実行では、署名でも読み戻しでも標準エラーは空だった。問いの最後の返答の形は、記事では < > の部分を書き換えて示す。
以下は、画像ファイルを C2PA の読み取りツール(c2patool)で読んだときの JSON の出力である。
この JSON に書かれていることだけで答えよ。JSON に無いことを推測で補わない。
Q1. この画像は AI で生成されたと記録されているか。次の4つから1つ選ぶ:
"AI で生成したと記録されている" / "AI で編集したと記録されている" / "AI 生成の記録は無い" / "判断できない"
Q2. この記録に署名したのは誰か。署名した証明書の組織名と共通名(common name)を答える。
Q3. この JSON から分かる範囲で、何が検証されていないか(確かめられていないこと)を箇条書きで答える。
次の形の JSON だけを返す:
{"q1": "<Q1 の答え>", "q1_reason": "<理由>", "q2_org": "<組織名>", "q2_common_name": "<共通名>", "q3": ["<項目>", "<項目>"]}
claude -p は、Claude Code を対話なしで動かす使い方である。モデルは --model sonnet で指定し、出力に記録されたモデル ID は claude-sonnet-5-5 だった。組み込みツールなし(--tools "")、MCP なし(空の設定と --strict-mcp-config)の最小の構成にして、ホームの外の空のフォルダから呼んだ。Claude Sonnet 5.5 の単価は、入力 $2・出力 $10(100 万トークンあたり、2026 年 11 月時点)である。
手元のモデルは、Ollama 0.40.13 で動かした gemma4:12b だ。Gemma 4 12B は Google DeepMind が 2026-06-03 に公開した Apache 2.0 のモデルで、使ったビルド(ダイジェスト 6114515d63c1)は 11.9B パラメータ・Q4_K_M の量子化、ダウンロードは 8.0GB である(ダウンロードの大きさは VRAM の使用量ではない)。思考は切り(think: false)、コンテキスト長(num_ctx)は 32768、温度は 0 にした。
同じ 2 つのモデルに電力ログを読ませた例は、家電ごとの電力ログを AI に読ませる — 生のログ・1時間の表・コードの実行で、答えの正しさはどう変わるか(2026-11-03 公開) にある。
正解の作り方
正解は、c2patool の JSON のキーから決めた。画像が本当に AI で生成されたかの正解ではない。今回の画像は ComfyUI で生成したものだが、それを JSON から確かめる手段は無いからだ。
- Q1:
active_manifestが指すマニフェストのc2pa.actions(またはc2pa.actions.v2)の先頭の動作が c2pa.created で、digitalSourceType が trainedAlgorithmicMedia なら「AI で生成したと記録されている」。3 つの JSON とも、これに当たる - Q2:
signature_infoのissuerとcommon_name。試験用の鍵の 2 枚は C2PA Test Signing Cert と C2PA Signer で、2026 年 1 月の画像は Google LLC と Google Media Processing Services - Q3: 次の (a)〜(e) のうち、その JSON で成り立つもの
| 項目 | 成り立つ条件(JSON のキー) | 正解の言い方 | 成り立った画像 |
|---|---|---|---|
| (a) 署名者の身元 | validation_status に signingCredential.untrusted がある | 署名した証明書が信頼リストに無く、署名者が誰かは確かめられていない | 3 つとも |
| (b) 署名の時刻 | タイムスタンプなし: success に timeStamp.validated が無い。あり: informational に timeStamp.untrusted がある | なし: 署名の時刻を第三者(タイムスタンプの発行元)が保証していない。あり: タイムスタンプの発行元が信頼リストに無い | 3 つとも |
| (c) 失効の確認 | informational に signingCredential.ocsp.skipped がある | 証明書が失効していないかをオンラインで確かめていない | 試験用の鍵の 2 枚 |
| (d) AI 開示の帰属 | c2pa.ai-disclosure に "created": true が無い | AI 開示の記述は署名者に帰属しない(gathered) | AI 開示を gathered にした画像だけ |
| (e) 内容の真偽 | 常に成り立つ(JSON のキーではなく仕様から決める) | 「AI で生成した」という記述が本当かは検証の対象ではない | 3 つとも |
(e) は JSON のキーから決めない項目なので、(a)〜(d) とは分け、(e) を含めた点と含めない点の両方を出した。さらに、次の答えを「誤り」として数えると、実行の前に決めておいた。
- success に claimSignature.validated があるのに「署名が無効」「署名が壊れている」と答える
- success に assertion.dataHash.match があるのに「改ざんされている」と答える
- validation_state が Valid なのに「Trusted(信頼できる署名者)」または「Invalid」と答える
- claim_generator_info のソフトを署名者として答える(Q2)
- 試験用の証明書を実在の組織の正式な署名として答える
- 「検証が通ったので AI 生成であることが証明された」と答える(Q1)
採点の結果
| 画像 | モデル | Q1 | Q2 | Q3 の正解の項目 | 挙げた項目((e) を除く) | Q3((e) を含めない) | Q3((e) を含める) | 誤り1〜6 | 費用(ドル) | 時間(秒) |
|---|---|---|---|---|---|---|---|---|---|---|
| AI 開示を created にした画像 | claude -p | 正解 | 正解 | a・b・c | a・b・c | 3/3 | 4/4 | 0 | 0.021666 | 13.1 |
| AI 開示を created にした画像 | gemma4:12b | 正解 | 正解 | a・b・c | a・c | 2/3 | 2/4 | 0 | — | 34.5 |
| AI 開示を gathered にした画像 | claude -p | 正解 | 正解 | a・b・c・d | a・c | 2/4 | 3/5 | 0 | 0.017916 | 8.8 |
| AI 開示を gathered にした画像 | gemma4:12b | 正解 | 正解 | a・b・c・d | a・c | 2/4 | 2/5 | 0 | — | 2.2 |
| 2026 年 1 月の画像 | claude -p | 正解 | 正解 | a・b | a・b | 2/2 | 3/3 | 0 | 0.020338 | 11.8 |
| 2026 年 1 月の画像 | gemma4:12b | 正解 | 正解 | a・b | a・b | 2/2 | 2/3 | 0 | — | 2.1 |
費用は claude -p が返した total_cost_usd で、小数 6 桁に丸めて示した(記録の値は 0.021665999999999998 と 0.020338000000000002 で、浮動小数点の表示の端数を落とした)。これはクライアント側の推定で、実際の請求額とは違うことがある。円には換算していない。gemma4:12b は手元の GPU で動かしたので API の費用は 0 で、電気代は含めていない。gemma4:12b の 1 回目の 34.5 秒は、この実行で最初の呼び出しで、モデルの読み込みを含むとみられる(読み込みの時間そのものは記録していない)。
差が出たのは「検証されていないこと」の挙げ方だった
6 回とも、Q1 と Q2 は正解で、誤り1〜6 に当たる答えは無かった。AI で生成したと記録されているかは digitalSourceType の値から、署名した証明書の組織名と名前は signature_info から、2 つのモデルとも取り出せた。Valid を「信頼できる署名者」と言い換えたり、主張を作ったソフト(swiftwand-provenance-test)を署名者として答えたりした回も無い。
差が出たのは Q3 だ。この実行では、JSON にコードとして書かれている項目は、2 つのモデルとも挙げた。signingCredential.untrusted((a))、signingCredential.ocsp.skipped((c))、2026 年 1 月の画像の timeStamp.untrusted((b))がそれに当たる。対して、JSON に「無い」ことから読む項目は落ちた。試験用の鍵の 2 枚でタイムスタンプが無いこと((b))に触れたのは、AI 開示を created にした画像での Claude だけだった。AI 開示が gathered で署名者に帰属しないこと((d))には、どちらのモデルも触れなかった。
仕様から決める (e) は、Claude が 3 つの画像すべてで挙げ、gemma4:12b はどの画像でも挙げなかった。Claude は、AI 開示を created にした画像で「Valid は署名者を信頼できるという意味ではない」とも自分から書いている。なお、2 つのモデルとも、2026 年 1 月の画像で signingCredential.expired を挙げた。これは (a)〜(e) の外の項目で、間違いではない。
一方で、Claude の答えには正解表の外で気になる点も 1 つあった。AI 開示を gathered にした画像で、issuer を「証明書を発行した側の組織名」と説明し、署名者の組織名そのものは JSON に記載がない、とも書いたのだ。c2pa-rs のコードでは、issuer には署名した証明書の組織名が入る(次の節で扱う)。Q2 は答えた組織名と共通名の値で採点したので正解に数え、誤り1〜6 の定義にも入らないので誤りとして数えていないが、読む側が注意すべき点である。
claude -p のように Claude Code を対話なしで呼び出して JSON を読ませる使い方は、基本の操作を押さえてからのほうが組み立てやすい。手を動かしながら進める入門書が1冊あると、設定や権限の考え方も確かめられる。
読み方 — 検証が通っても「AI 生成」が証明されたわけではない

Valid が保証するのは、記録がこのファイルに結び付き、改ざんされていないこと
Valid の JSON を見て「AI で生成されたことが証明された」と読むのは誤りだ。仕様の指針は、検証が見るのは記録の内容の良し悪しではなく、次の点だけだと書く。
merely whether the assertions included within can be validated as associated with the underlying asset, correctly formed, and free from tampering.
(出典: C2PA Technical Specification 2.5、CC BY 4.0)
つまり、検証で確かめられるのは、アサーションがこのファイルに結び付いていること、形が正しいこと、改ざんされていないことである。「AI で生成した」という digitalSourceType の値は、署名する側が --create や manifest の JSON で指定したもので、作り手の宣言だ。今回も、その値を書いたのは当サイトの実験のスクリプトだった。Valid から言えるのは、その宣言が署名の後に書き換えられていない、というところまでである。
署名者と、主張を作ったソフトは別の項目
JSON には「誰が」に当たる項目が 2 つある。1 つは signature_info で、署名した証明書の情報だ。もう 1 つは claim_generator_info で、主張を作ったソフトの名前と版が入る。今回の JSON では、前者が C2PA Test Signing Cert と C2PA Signer で、後者が swiftwand-provenance-test の 0.1 だった。
仕様は、Valid なら主張は主張を作ったソフトに帰属すると書き、created のアサーションは署名者に帰属すると書く。ただ、主張を作ったソフトの名前は、今回もこの実験のために manifest の JSON に書いた名前がそのまま入った。署名者の欄と、ソフトの名前の欄を取り違えないことが、読むときの最初の注意になる。
issuer という名前の項目に入っていたのは、署名した証明書の組織名
もう一つ紛らわしいのが signature_info.issuer である。CAI の文書(SDK object reference の Reader のスキーマ)は、issuer を「Human-readable issuing authority for this signature.」と説明している。ところが c2pa-rs のコードは、ここに署名した証明書の subject の組織名を入れる。
実際、今回の実行で issuer という名前の項目に入っていたのは、署名した証明書の組織名「C2PA Test Signing Cert」だった。試験用の証明書を発行した中間の認証局の組織名は「C2PA Test Intermediate Root CA」で、こちらは JSON に出てこない。コードのとおりなら、2026 年 1 月の画像の「Google LLC」も、署名した証明書の組織名である。
Claude が issuer を「証明書を発行した側の組織名」と説明したのは、文書の言い方に沿った読み方ではある。しかし、少なくとも c2patool 0.28.4 の今回の出力では、この項目を「発行した認証局」と読むと実際とずれる。
untrusted は「偽物」ではなく「検証した側の信頼リストに無い」
signingCredential.untrusted を、仕様の付録 D は「The signing credential is not listed on any of the validator’s applicable trust lists.」と説明している。検証した側の信頼リストに無い、という意味で、署名が偽物だという意味ではない。今回は信頼リストを何も渡していないので、試験用の証明書でも Google LLC の証明書でも、この判定になった。
Trusted になるのは、署名した証明書が、検証する側の信頼リストにある認証局につながる場合である。C2PA Trust List の認証局から証明書を受けられるのは、FAQ によれば適合プログラムで承認された製品だ。本記事は本物の証明書での署名を試しておらず、Trusted になる場合の表示は確かめていない。
タイムスタンプと失効の確認
試験用の鍵の 2 枚にはタイムスタンプが無く、署名の時刻は第三者に保証されていない。また、informational の signingCredential.ocsp.skipped は、証明書が失効していないかをオンライン(OCSP)で確かめなかったことを示す。仕様は、検証する側がオンラインの OCSP の確認をしないと決めたときに、この情報のコードを出すよう定めている。c2pa-rs の既定では、失効の情報を取りに行かない(ocsp_fetch: false)。
タイムスタンプが無いことは、JSON のどこにもコードとして出ない。失効の確認を省いたことも、情報のコードとして出るだけで、validation_status には出ない。どちらも、失敗のコードだけを見ていると気づかない点である。
AI に読ませるときは、JSON に「無い」ことを問いに書く
この実行で 2 つのモデルが落としたのは、JSON に書かれていないことから読む項目だった。そこで AI に C2PA の JSON を説明させるなら、「タイムスタンプはあるか」「AI 開示のアサーションに created があるか」「issuer はどの証明書のどの項目か」を問いに書いて、一つずつ確かめる形が考えられる。ただし、この問いの形は本記事では試していない。
また、AI の説明は JSON を読む手助けにとどまる。検証の状態と、success・informational・failure のコードの一覧は、c2patool の出力そのものを見て確かめる。この実行でも、Q1 と Q2 のように JSON の値をそのまま取り出す問いでは 6 回とも正解だったが、Q3 では項目の取りこぼしがあった。
この実測で言えないこと

結果を読むときの限界を、まとめて書いておく。
- 各条件 1 回の実行: 画像は 2 枚(署名は 1 枚に 2 条件)、AI は 2 つのモデルで各 1 回だけ読ませた。答えの揺れは測っていない。数はすべて「この実行ではこうだった」という値で、モデルの一般的な能力を示すものではない
- 正解は JSON から作った: 採点の正解は c2patool の JSON のキーと仕様から決めたもので、画像が本当に AI で生成されたかの正解ではない
- 試験用の鍵だけ: 本物の証明書は使っておらず、Trusted になる場合は確かめていない。信頼リストも渡していない。Verify と Inspect には上げておらず、その表示は文書によるもの
- 大きさと時間: 1 枚の PNG・各 1 回の値で、ほかの画像や形式、PC での値ではない。タイムスタンプ(ta_url)を付けた場合と、失効の情報を取りに行く場合は測っていない
- 署名の後の加工: 署名したファイルを縮小・保存し直したときに検証がどう変わるかは、本記事では扱っていない
- 版: c2patool 0.28.4 の出力の specVersion は 2.4.0 だった。この結果から「c2patool は仕様 2.5 に対応」とは言えない
- c2pa-python: Python から扱う c2pa-python(MIT OR Apache-2.0)は使っていない。PyPI の最新の 0.38.2(2026-10-26)の配布物は macOS の wheel 2 本だけで、Windows の wheel がある最新は 0.38.0(2026-09-29)である。分類子には Windows も挙がっている
- モデルの知識: 2 つのモデルが C2PA の仕様を学習でどこまで知っているかは確かめていない
- 法: 本記事は法的助言ではない。この署名で法の義務を満たせるかどうかは扱っていない
gemma4:12b のような手元のモデルで読ませ比べる実験を自分でも試すなら、ノート PC でローカル LLM を動かす手順をまとめた本が出発点になる。
まとめ — C2PA の記録は、状態・コード・署名者・宣言を分けて読む

- C2PA で「AI で生成した」を示すのは、
c2pa.actionsの先頭の c2pa.created に付けた digitalSourceType(trainedAlgorithmicMedia)である。c2pa.ai-disclosure(2.4 で追加、必須は modelType)は、どのモデルを使ったかを書く別のアサーションだ - 当サイトの ComfyUI の画像に c2patool 0.28.4 と試験用の鍵で署名すると、この実行では Valid で、署名者は信頼リストに無い(signingCredential.untrusted)と出た。ファイルは 59,226 バイト(6.8%、サムネイルを含む)増え、署名は直後の検証を含めて 0.09 秒だった
- 2026 年 1 月の画像の Google LLC の記録は、期限切れ(signingCredential.expired)が加わって Invalid だった。署名とデータのハッシュは一致しており、画像が偽物という意味ではない
- 仕様の Well-Formed・Valid・Trusted と、c2patool の Invalid・Valid・Trusted は名前がずれる。c2patool(c2pa-rs)は、失敗が untrusted だけなら Valid として扱う
- JSON を読ませた
claude -pと gemma4:12b は、Q1・Q2 に 6 回とも正解し、誤り1〜6 は 0 だった。「何が検証されていないか」では、内容の真偽((e))に触れたのは Claude だけで、AI 開示が署名者に帰属しないこと((d))にはどちらも触れなかった - Valid が保証するのは、記録がファイルに結び付き、改ざんされていないことまでで、AI 生成の記録は作り手の宣言である。今回の出力では、issuer の項目に署名した証明書の組織名が入っていた
- 本記事は法的助言ではない
参照
- C2PA Specifications 2.5(版の一覧)
- Content Credentials : C2PA Technical Specification 2.5
- IPTC NewsCodes「Digital Source Type」
- contentauth/c2patool(GitHub)
- c2patool v0.28.4 のリリース
- c2patool docs/usage.md(v0.28.4)
- c2patool docs/manifest.md(v0.28.4)
- c2patool docs/signing.md(v0.28.4)
- c2pa-rs sdk/src/validation_results.rs(c2pa-v0.91.7)
- c2pa-rs sdk/src/settings/mod.rs(c2pa-v0.91.7)
- c2pa-rs sdk/src/settings/builder.rs(c2pa-v0.91.7)
- c2pa-rs sdk/src/cose_validator.rs(c2pa-v0.91.7)
- CAI open-source SDK の文書「Introduction」(opensource.contentauthenticity.org)
- SDK object reference: Reader のスキーマ(opensource.contentauthenticity.org)
- Trust lists(opensource.contentauthenticity.org)
- C2PA の FAQ
- Creator Assertions Working Group(CAWG)
- Content Credentials の確認サイト(Verify)
- PyPI の c2pa-python
- Anthropic の Models overview
- Anthropic の Pricing
- Claude Code の CLI reference
- Claude Code の非対話の実行(headless)
- Ollama のモデルページ gemma4:12b
- Ollama の文書「Thinking」
- Google「Introducing Gemma 4 12B」
- Google「Apache License 2.0|Gemma」





