お断り: 本記事は C2PA Technical Specification v2.3(2026年4月時点)を筆者が読解して整理したものです。仕様は継続的に更新されており、細部の記述は変更される可能性があります。実装や設計判断の根拠として用いる際は、必ずC2PA 公式仕様をご確認ください。本記事に誤りや古くなった箇所を見つけられた場合は、記事末尾のフィードバック枠よりお知らせいただけると助かります。
はじめに
本記事は「C2PA 実装入門」シリーズの第2回です。第1回では「なぜ来歴証明が必要なのか」を押さえました。今回は C2PA が技術的にどう動いているのかを、一連のフローとして俯瞰しておきます。
C2PA の仕様書(Section 2.1 Actors and Actions)では、以下の役割が定義されています。
- Claim generator: Claim を生成するハードウェアまたはソフトウェア(カメラ、編集ソフト、AI 生成ツールなど)
- Signer: Claim の署名に使う秘密鍵の保持者。Claim generator が Signer の鍵を使って署名する
- Manifest consumer: 来歴データを取得する目的で Manifest 付きアセットを消費する人やシステム
- Validator: Manifest consumer のうち、暗号署名やハッシュの検証を実行する役割
本記事ではこの役割分担に沿って、Claim generator / Signer 側がコンテンツに署名を付与し、Manifest consumer / Validator 側がそれを検証する、という一連のフローを追いかけていきます。
署名から検証までの全体フロー
C2PA におけるコンテンツのライフサイクルを 1 本のフローで描くと、こんな感じです。
以降、各ステップをもう少し掘り下げていきます。
ステップ 1-2: コンテンツ作成と Assertion の記録
コンテンツが作られたタイミング、もしくはその直後に、来歴に関する事実が Assertion として書き残されます。
カメラでの撮影なら「撮影した」という事実が c2pa.actions 内のアクション c2pa.created として記録されます(撮影条件の EXIF は別の Assertion stds.exif として独立して格納されます)。Photoshop での編集なら「切り抜いた」(c2pa.cropped)「色調を変えた」(c2pa.adjustedColor)といった操作が c2pa.actions の中に並びます。プリディファインドのアクション名一覧は仕様書の Table 8 “List of pre-defined actions” にまとまっています。
ポイントは、各 Assertion が独立したドキュメントになっていて、「いつ・誰が・何をしたか」を細かい粒度で残す設計になっているところです。なお、一度 Claim の一部に組み込まれた Assertion はあとから書き換えられません(仕様書に「they cannot be modified once made as part of a claim」と明記されています)。先行 Manifest の Assertion を取り除きたいときは、Redaction という正式な手続きを通して、「何を消したか」の記録を残しつつ行います。
ステップ 3: Claim の構築
作成された Assertion 群を「1つの署名単位」としてまとめるのが Claim です。仕様書の定義では “a digitally signed and tamper-evident data structure that references a set of assertions” とあり、Claim は Assertion の実データを直接含まず、代わりに各 Assertion への hashed URI(ハッシュ付き参照)のリストを保持します。CDDL スキーマ上もフィールドの型は $hashed-uri-map(URI + ハッシュ値の組)であり、Assertion そのものではありません。
Claim v2 では、このリストが created_assertions(署名者が責任を持つもの)と gathered_assertions(他の工程から引き継いだもの)の 2 系統に分けられていて、「署名者は何に責任を持っているのか」が構造として見える形になっています。
ステップ 4: Claim への署名
Claim が組み上がったら、署名者の秘密鍵で COSE_Sign1 形式の署名を載せます。署名と一緒に、署名者の X.509 証明書チェーンも Claim Signature に同梱されます。
RFC 3161 準拠のタイムスタンプ局(TSA)から署名時刻の証明を取得して埋め込む流れも仕様に組み込まれていて、「いつ署名されたか」を第三者が裏付けできる形になっています。
署名者の証明書は C2PA Trust List に登録された認証局(CA)から発行されている必要があり、ここが C2PA の信頼モデルの起点です。信頼モデルの中身は第3回の Claim Signature セクションで扱います。
ステップ 5: Manifest の埋め込み
Assertion Store(Assertion 群の入れ物)、Claim、Claim Signature の 3 つがそろったら、これらを JUMBF(JPEG Universal Metadata Box Format)というコンテナにまとめて、コンテンツファイルの中にそのまま埋め込みます。
ファイル(JPEG / PNG / MP4 / PDF ...)
├── コンテンツ本体(画素データ、映像、音声等)
└── JUMBF メタデータ
└── Manifest Store
└── Manifest
├── Assertion Store
├── Claim
└── Claim Signature
メタデータがコンテンツと一体化するので、ファイルをコピーしたり転送したりしても来歴情報がそのまま残ります。「来歴メタデータを外部サーバーに置く」方式とは対照的で、ファイル単独で完結するのが C2PA の大きな持ち味です。
各ファイルフォーマットごとの埋め込み方法(JPEG なら APP11、PNG なら専用チャンク、MP4 なら ISO BMFF box など)は、仕様書の Appendix A “Embedding manifests” にまとまっています。
ステップ 6: 配信経路での注意点
署名済みコンテンツが SNS やメッセージアプリを経由して配信される場面では、メタデータが落とされるリスクがついて回ります。プラットフォーム側で画像を再エンコードしたり、メタデータを問答無用で除去してきたりするためです。
この問題に対して C2PA は 2 つの逃げ道を用意しています。
- Soft Binding: 知覚的ハッシュや電子透かしを使い、メタデータが剥がれても「元のコンテンツと同一だ」ということを緩やかに紐づけておく仕組み
- Content Credentials Cloud: メタデータのコピーをクラウド側にも持っておき、メタデータが落ちたコンテンツをフィンガープリントで突き合わせて復元する仕組み
配信経路の設計は実運用上の大きな論点なので、第7回で改めて深掘りします。
ステップ 7: 検証
受け取ったコンテンツの来歴を確かめるのが検証ステップです。仕様書では Chapter 15 “Validation” にまとまっており、Section 15.1.2 “Phases of Validation” で以下のフェーズが定義されています。仕様書はこれらのフェーズについて “listed in no particular order”(順序は特に定めない)と書いているので、実装ごとに走る順番は変わってきます。
| フェーズ | 仕様書セクション | 検査内容 |
|---|---|---|
| Assertion の検証 | 15.10 | Claim に列挙された hashed URI と Assertion Store のハッシュが一致するか |
| アセットの内容の検証 | 15.12 | コンテンツ本体のハッシュと Hard Binding Assertion の値が一致するか |
| 署名の検証 | 15.7 | Claim Signature の暗号署名が正当か |
| タイムスタンプの検証 | 15.8 | RFC 3161 タイムスタンプの検証 |
| 証明書失効情報の検証 | 15.9 | 署名者の証明書が失効していないか |
| Ingredient の検証 | 15.11 | 取り込まれた素材の Manifest が正当か |
仕様書には Figure 13 “Validating a Claim” としてこのプロセスのフロー図も載っています。
ここまではすべて C2PA SDK(c2pa-rs、c2pa-python など)が標準で面倒を見てくれる範囲です。これに加えて、アプリケーション固有のポリシー判定(例: AI 生成コンテンツは社内配信のみ許可する、特定の認証局で署名された素材だけ採用する など)は仕様の外側で、SDK の外で自前で組む領域になります。検証の前提として JUMBF メタデータをファイルから取り出す処理もありますが、こちらも仕様上の Validation フェーズには含まれず、SDK が裏で勝手にやってくれます。
検証結果は Section 14.3 “Validation states” で定義される 3 段階(Well-Formed → Valid → Trusted)で評価されます。検証の実装については第6回で SDK を使って実際にコードを書いていきます。
既存の Manifest がある素材を取り込むとき
コンテンツの編集ワークフローでは、「すでに C2PA 署名が付いた素材を読み込み、編集して、新しい Manifest 付きで書き出す」という場面がよく出てきます。
このとき、元素材の Manifest は Ingredient(素材)として、新しい Manifest の中に参照の形で取り込まれます。こうしておくと、最終的なコンテンツから遡って元素材の来歴まで辿れるので、編集の連鎖を時系列で追跡できる、というわけです。
写真 (Manifest A)
↓ Photoshop で編集
編集済み画像 (Manifest B, Ingredient: Manifest A)
↓ CMS に投稿
記事掲載画像 (Manifest C, Ingredient: Manifest B)
この「Manifest のチェーン」は、Manifest Store の中に複数の Manifest が並ぶ形で表現されます。
SDK とツールの選択肢
C2PA の署名と検証を実装するためのオープンソースツールがいくつか出ています。
| ツール | 言語 | 用途 |
|---|---|---|
| c2pa-rs | Rust | 参照実装。最も機能が充実 |
| c2patool | CLI | c2pa-rs ベースのコマンドラインツール |
| c2pa-python | Python | c2pa-rs の Python バインディング。データパイプライン・AI ワークフロー向け |
| c2pa-node-v2 | Node.js / TypeScript | c2pa-rs ベースの Node.js バインディング |
このシリーズでは主に c2patool(第4回)と c2pa-rs(第5回以降)を使って実装を進めます。
まとめと次回予告
C2PA の署名から検証までのフローをざっと辿りました。要点は 3 つです。
- 「事実の記録(Assertion)→ 束ねて署名(Claim + Signature)→ ファイルに埋め込み(JUMBF)→ 検証」という一方向の流れ
- メタデータとコンテンツがファイル内で一体化するので、ファイル単独で来歴が完結する
- 検証のうち暗号処理は SDK が面倒を見てくれるが、ポリシー判定は自前で設計する必要がある
続く第3回 Manifest の内部構造を理解するでは、このフローの中心にいる Manifest の中身を分解して、Assertion・Claim・Claim Signature・JUMBF の 4 レイヤーがそれぞれどんな役割を担っているかを覗いていきます。
参考リンク
TechThanks は Content Credentials の実装支援に取り組んでいます。コンテンツ来歴技術の導入検討やワークフロー設計について、お気軽にご相談ください。