お断り: 本記事は C2PA Technical Specification v2.3(2026年4月時点)および c2pa-rs の公式ドキュメントとソースコードを筆者が読解して整理したものです。SDK のバージョンアップにより API が変更される可能性があります。実装の根拠として用いる際は、必ず各ライブラリの最新ドキュメントおよび C2PA 公式仕様をご確認ください。本記事に誤りや古くなった箇所を見つけられた場合は、記事末尾のフィードバック枠よりお知らせいただけると助かります。

はじめに

本記事は「C2PA 実装入門」シリーズの第6回です。第5回では SDK を使ってコンテンツに Manifest を付与する方法を扱いました。本記事では署名されたコンテンツを受け取る側の視点に立ち、検証パイプラインの設計と実装に進みます。

第4回の末尾で整理した検証の6ステップを、c2pa-rs の Reader API で実際にコードに落とし込んでいきます。本番運用に向けた証明書や運用設計の話題は第7回で扱います。

検証の全体像

C2PA Manifest の検証は、以下の6ステップで構成されます。SDK が自動的に処理するステップ(1〜5)と、ユースケースごとに自前で設計するステップ(6)に分かれます。

ステップ内容担当
1. JUMBF パースManifest Store の取り出しと構造妥当性の確認SDK
2. ハッシュ照合Claim 内の hashed URI と Assertion Store の整合性検証SDK
3. Hard Binding 検証アセット本体のハッシュと c2pa.hash.data の一致確認SDK
4. 署名検証COSE_Sign1 の署名値の暗号学的検証SDK
5. Trust List 判定署名証明書チェーンの信頼性と失効状態の検証SDK
6. ポリシー判定Assertion の内容に基づくビジネスロジックの適用自前実装

SDK を呼び出すだけでステップ1〜5の結果が validation_results として返ってくるため、開発者はステップ6のポリシー判定に集中できます。ただし、SDK が返す検証結果を正しく解釈し、エラー時の処理を適切に設計することが不可欠です。

Reader API で Manifest を読み出す

c2pa-rs の Reader API を使い、画像ファイルから Manifest Store を読み出す基本コードです。

use c2pa::Reader;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut file = File::open("signed_image.jpg")?;
    let reader = Reader::from_stream("image/jpeg", &mut file)?;

    // Manifest Store 全体を JSON で出力
    println!("{}", reader.json());

    // アクティブな Manifest のラベルを取得
    if let Some(manifest) = reader.active_manifest() {
        println!("Active Manifest: {}", manifest.label());
        println!("Title: {:?}", manifest.title());
    }

    Ok(())
}

Reader::from_stream は読み込みと同時にステップ1〜5の検証を実行します。検証に失敗してもエラーにはならず、結果は validation_results に格納されます。つまり、Reader の生成が成功したからといって Manifest が信頼できるわけではありません。必ず検証結果を確認する必要があります。

validation_results の解釈

Reader から取得できる検証結果には、成功・情報・失敗の3カテゴリがあります。

// 検証状態を確認
let state = reader.validation_state();
println!("Validation state: {:?}", state);

// 詳細な検証結果を確認
if let Some(results) = reader.validation_results() {
    // 成功したステップ
    for success in &results.active_manifest.success {
        println!("[OK] {}: {}", success.code, success.explanation);
    }
    // 失敗したステップ
    for failure in &results.active_manifest.failure {
        println!("[NG] {}: {}", failure.code, failure.explanation);
    }
}

validation_state()ValidationState 列挙型を返し、大まかな状態を示します。

  • Valid: 署名とハッシュは正しいが、証明書が Trust List に載っていない
  • Trusted: 署名・ハッシュ・証明書チェーンすべてが検証を通過
  • Invalid: 改ざん検知やパースエラーなど、構造的な問題あり

代表的な code の値と意味は以下の通りです。

code意味
claimSignature.validatedClaim Signature の署名値が正しい
assertion.hashedURI.matchClaim から Assertion への参照が改変されていない
assertion.dataHash.matchアセット本体のハッシュが一致
timeStamp.validatedRFC 3161 タイムスタンプの検証成功
signingCredential.untrusted署名証明書が Trust List に載っていない
signingCredential.revoked署名証明書が失効している
assertion.dataHash.mismatchアセット本体が改ざんされている

Trust List の設定

デフォルトでは、c2pa-rs は C2PA が公開している Trust List に対して証明書チェーンを検証します。独自の Trust List を使いたい場合や、テスト用証明書を信頼済みにしたい場合は、設定で上書きできます。

use c2pa::settings::Settings;

// カスタム Trust List を設定(PEM 形式の証明書を追加)
let trust_anchors = std::fs::read_to_string("custom_trust_list.pem")?;
let settings = Settings::default()
    .set_trust_anchors(&trust_anchors);

本番環境では、C2PA Trust List に登録された認証局から発行された証明書を使用します。Trust List の管理は C2PA Conformance Program が担当しており、定期的に更新されます。

ポリシー判定の設計

ステップ6のポリシー判定は、組織やユースケースに応じて自前で設計する領域です。Manifest 内の Assertion を読み取り、受け入れ可否を判定するロジックを実装します。

例1: AI 生成コンテンツの振り分け

fn check_ai_policy(manifest: &c2pa::Manifest) -> bool {
    for assertion in manifest.assertions() {
        if assertion.label() == "c2pa.actions.v2" {
            let data: serde_json::Value =
                serde_json::from_slice(assertion.data()).unwrap();
            if let Some(actions) = data["actions"].as_array() {
                for action in actions {
                    let source_type = action["digitalSourceType"]
                        .as_str().unwrap_or("");
                    if source_type.contains("trainedAlgorithmicMedia") {
                        // AI 生成コンテンツ: 社内利用のみ許可
                        return false;
                    }
                }
            }
        }
    }
    true  // 非 AI 生成: 外部配信も許可
}

例2: 特定 CA で署名された素材のみ採用

fn check_ca_policy(manifest: &c2pa::Manifest) -> bool {
    if let Some(sig_info) = manifest.signature_info() {
        let issuer = sig_info.issuer.as_deref().unwrap_or("");
        // 許可リストに含まれる CA かどうかを判定
        let allowed_issuers = ["DigiCert", "GlobalSign"];
        return allowed_issuers.iter()
            .any(|allowed| issuer.contains(allowed));
    }
    false
}

例3: AI 学習オプトアウトの確認

fn check_training_mining(manifest: &c2pa::Manifest) -> bool {
    for assertion in manifest.assertions() {
        if assertion.label() == "cawg.training-mining" {
            let data: serde_json::Value =
                serde_json::from_slice(assertion.data()).unwrap();
            let training = &data["entries"]["cawg.ai_generative_training"]["use"];
            if training == "notAllowed" {
                return false; // 学習利用は不可
            }
        }
    }
    true
}

CAWG identity の追加検証

第4回で確認したように、cawg.identity は Claim Signature とは独立した署名を持つ Assertion です。検証パイプラインで作成者の身元情報まで確認したい場合は、この Assertion を個別に検証する必要があります。

cawg.identity 内の referenced_assertions には、作成者が追認した Assertion の hashed URI が列挙されています。Claim Signature が「パブリッシャー」の信頼を、cawg.identity が「クリエイター」の信頼を、それぞれ独立した鍵で形成しているため、ポリシー判定では両方を組み合わせた判断が可能です。たとえば「特定の組織に所属するクリエイターが署名した素材のみ採用する」といったルールが実装できます。

エラーハンドリング

検証パイプラインでは、様々な失敗パターンに対して適切に対応する設計が必要です。

改ざん検知時

assertion.dataHash.mismatch が返された場合、アセット本体が署名後に改変されています。この場合は Manifest の情報を一切信頼せず、改ざんが検知された旨をログに記録した上で、後段の処理に渡さないようにします。

証明書期限切れ・失効時

signingCredential.revoked や証明書の有効期限切れが検出された場合の対処は、ユースケースによって異なります。ニュース素材であれば署名時点のタイムスタンプが有効期限内であったことを確認すればよい場合もありますし、法的証拠としての利用であれば厳密に拒否すべき場合もあります。RFC 3161 タイムスタンプが付与されていれば、署名時点での証明書の有効性を確認できます。

Manifest が存在しない場合

C2PA Manifest が埋め込まれていないコンテンツをどう扱うかも設計上の判断ポイントです。「Manifest なしは受け入れない」「Manifest なしでも受け入れるが、来歴不明としてフラグを立てる」など、組織のポリシーに応じて決定します。

まとめ

C2PA の検証パイプラインは、SDK が担当する暗号学的検証(ステップ1〜5)と、開発者が設計するポリシー判定(ステップ6)の2層構造になっています。Reader API を呼び出すだけで JUMBF パースからハッシュ照合・署名検証・Trust List 判定までが一括で実行されるため、開発者は validation_results の解釈とポリシー判定ロジックの設計に注力できます。

ポリシー判定の具体的な設計はユースケースに依存しますが、c2pa.actions.v2digitalSourceType による AI 生成判定、cawg.training-mining による学習オプトアウト確認、署名者の CA による振り分けなどが典型的なパターンです。

次の第7回では、テスト環境から本番環境への移行に必要な証明書取得、ワークフロー設計、パフォーマンス考慮、規制動向について解説します。

参考リンク


TechThanks は Content Credentials の実装支援に取り組んでいます。C2PA SDK の導入や検証パイプラインの設計についてお気軽にご相談ください。