お断り: 本記事は AWS KMS Cryptographic DetailsAWS KMS Sign APIc2pa-rs(c2patool) の公式ドキュメントとソースコードを筆者が読解して整理した、ハンズオンの構成記事です。コマンド・パラメータは AWS SDK や c2patool のバージョンによって挙動が変わる可能性があります。実機で試す際は、必ずAWS KMS 公式ドキュメントと c2patool の最新ヘルプをご確認ください。本記事に誤りや古くなった箇所を見つけられた場合は、記事末尾のフィードバック枠よりお知らせいただけると助かります。

はじめに

本記事は「C2PA 実装入門」シリーズの発展編です。前回の記事では、OpenSSL で自前の Root CA と EE 証明書を発行し、ローカルの秘密鍵ファイルで c2patool に署名させて、トラストアンカーに登録することで signingCredential.trusted まで持っていきました。この構成は学習用途やローカル検証としては適しているものの、本番環境での運用を想定した場合、署名鍵(秘密鍵)をローカルファイルシステム上に平文で保持し続ける運用はセキュリティリスクが高く、推奨されません。

そこで本記事では、署名鍵を AWS KMS(Key Management Service)に配置し、c2patool からは KMS の API を通じて暗号署名を呼び出すアーキテクチャを構築します。さらに、署名鍵(リーフ鍵)のみならず Root CA の秘密鍵までも AWS KMS 内に保持させることで、ローカル環境上に秘密鍵を一切保存・展開しない高セキュアな構成を実現します。非対称鍵の生成から証明書の発行、c2patool --signer-path を用いた KMS 連携署名、そして検証に至る全工程をハンズオン形式で解説します。

なお、本記事は AWS の基礎的な概念や操作(アカウント作成、IAM ユーザー / ロールの設定、AWS CLI のセットアップ、リージョンの選択など)を習得済みであることを前提としています。AWS そのものの入門解説は本記事の対象外としていますので、必要に応じて AWS 公式ドキュメント をご参照ください。

なぜ AWS KMS で署名するのか

AWS KMS は AWS が提供するフルマネージドな暗号鍵管理サービスです。非対称キー(Asymmetric Key)として ECDSA や RSA の鍵ペアを生成することで、kms:Signkms:Verify といった API 経由での署名・検証演算が可能となります。C2PA で標準的に採用される ES256(ECDSA P-256 + SHA-256)は、KMS のキー仕様 ECC_NIST_P256 および署名アルゴリズム ECDSA_SHA_256 と完全に対応しています。

ローカル鍵運用で困ること

ローカルファイルに秘密鍵を保持する構成では、本番運用を考慮した際に以下のような重大な運用課題に直面します。

  • 秘密鍵がファイルとしてローカルに置かれており、cat 等で容易に閲覧・複製が可能
  • 誰がいつ署名演算を実行したかという厳密な証跡が残らない(OS プロセスログやアプリログ程度にとどまる)
  • 署名権限の制御が OS のファイル読み取り権限に依存し、アクセス制御の粒度が粗い
  • 鍵のバックアップや冗長化を自前で運用設計・実装する必要がある

KMS にすると何ができるか

これらの課題は、AWS KMS のマネージド機能を活用することで根本的に解決できます。

  • 鍵の生成と保管が KMS 内部で完結し、ローカルファイルシステム上に秘密鍵が存在しない
  • kms:Sign API は署名対象データを送信して署名値のみを受け取る。秘密鍵自体を外部にエクスポートする API は提供されない(GetPublicKey は公開鍵のみ取得可能)
  • 誰がいつ署名したかの監査証跡が AWS CloudTrail に自動記録される
  • IAM ポリシーによって「特定ロールには署名のみを許可し、鍵の更新・削除は禁止する」といった最小権限の原則を厳格に適用可能
  • 鍵のバックアップやレプリケーションは KMS のマルチリージョン機能等により AWS のマネージドインフラ上で担保される

構成移行によるメリットの総括

ローカル鍵の運用で課題となっていた「秘密鍵の漏洩リスク」「監査証跡の欠落」「アクセス制御の粗さ」「バックアップ運用の負荷」という 4 つの課題が、鍵管理を KMS へ移管するだけで一掃されます。アプリケーション側の実装も「ローカルの秘密鍵で署名する処理」を「KMS の Sign API を呼び出す処理」へ置き換えるのみであり、c2patool 側のインターフェース(--signer-path 経由)に影響を与えません。

なお、AWS KMS の内部基盤には FIPS 140 認定のハードウェアセキュリティモジュール(HSM)が採用されていますAWS KMS Cryptographic Details 参照)。利用者視点では使い勝手の良い Web API でありながら、秘密鍵が外部に決して平文出力されない堅牢なハードウェア保護の恩恵を直接享受できます。

必要なもの

項目用途
AWS アカウント(KMS 利用権限)鍵保管
AWS CLI v2AWS 認証
miseTerraform / Python / uv の導入とタスクランナー
c2patool 0.26.50 以降外部 signer 対応の c2patool(cargo install c2patool

具体的な Terraform / Python のバージョンは .mise.toml で固定し、Python パッケージは uv でプロジェクト直下の .venv/ に入れます。システム側の Python / Terraform は触らない構成です。

全体フロー

本ハンズオンは以下の 4 ステップで進めます。KMS 上に 2 つの非対称鍵(CA 鍵および署名鍵)を生成し、X.509 証明書を KMS 内の CA 鍵で署名・発行した上で、c2patool が外部 signer スクリプトを介して KMS に署名を委譲する構成です。

flowchart TD subgraph S1["Step 1: KMS で鍵を 2 つ作る"] KMSCA[("AWS KMS
CA 鍵
(alias/c2pa-rootca)")] KMSLF[("AWS KMS
署名鍵
(alias/c2pa-signer)")] end subgraph S2["Step 2: 証明書を発行(全部 KMS で署名)"] CACERT["ca.pem
(自己署名 Root)"] LFCERT["kms-signer.pem
(EE)"] KMSCA -->|自己署名| CACERT KMSCA -->|EE を発行| LFCERT KMSLF -->|公開鍵を埋め込み| LFCERT end subgraph S3["Step 3: c2patool で署名"] IMG["input.jpg"] SIGNER["kms-signer.py
(external signer)"] OUT["signed.jpg
(Manifest 入り)"] IMG --> SIGNER SIGNER -->|kms:Sign API| KMSLF SIGNER --> OUT end subgraph S4["Step 4: 検証"] VER{"c2patool で
チェーン照合"} NG["untrusted"] OK["trusted"] VER -->|アンカー無し| NG VER -->|ca.pem をアンカーに登録| OK end LFCERT -->|chain.pem として渡す| SIGNER OUT --> VER CACERT -->|trust --trust_anchors| VER

ローカルファイルで署名を行っていた前回の構成と比較すると、Step 1 で鍵の生成と保管をすべて KMS へ集約し、Step 2 および Step 3 の暗号署名処理を KMS API の呼び出しへと変更しています。Step 4 の検証プロセスについては、前回の手順をそのまま適用できます。

実行手順

本記事の Terraform / Python / .mise.toml 一式は TechThanks/c2pa-aws-kms-signing にまとめてあります。リポジトリをクローンし、mise をセットアップすることで、以下の手順に沿って即座に実行可能です。各ステップの解説では、リポジトリ内の該当コードへのリンクも併記しています。

なお、本手順で用いる mise run xxx コマンドは、.mise.toml[tasks.xxx] セクションに定義されたタスクランナー経由で実行されます。各タスクが実行する具体的なシェルコマンドを確認したい場合は、.mise.toml の定義を参照してください。

0. SSO プロファイルを用意して AWS にログイン

初回だけ標準形式の SSO プロファイルを 1 つ作っておきます。

aws configure sso

対話プロンプトで以下を入力します。

質問入力
SSO session namec2pa
SSO start URL利用する AWS Identity Center の https://d-XXXX.awsapps.com/start
SSO regionap-northeast-1
SSO registration scopesデフォルト(Enter)
(ブラウザ認可後) アカウント / ロール選択検証に使うアカウントと AWSAdministratorAccess
Default client Regionap-northeast-1
Default output formatjson
CLI profile namec2pa

完了したら次のように使います。

aws sso login --profile c2pa        # 期限切れになったら都度
export AWS_PROFILE=c2pa             # mise タスクのサブシェルにも引き継がれる
aws sts get-caller-identity         # 想定アカウントが返ることを確認

以降の mise run tf-* などはこのシェルで実行してください。新しいシェルを開くたびに export AWS_PROFILE=... が必要です(.envrc / direnv 等を使うと自動化できます)。

1. ツール導入と Python 依存

mise trust .                 # .mise.toml を信頼(初回のみ)
mise install                 # Terraform 1.10 / Python 3.12 / uv をインストール、.venv も自動作成
mise run install             # uv sync で pyproject.toml + uv.lock から .venv にインストール

.mise.toml で Terraform / Python / uv のバージョンを固定し、Python パッケージは pyproject.toml + uv.lock から uv が venv に入れます。設定全体は .mise.tomlpyproject.toml を参照してください。

2. KMS 鍵を作成

mise run tf-init             # 初回のみ
mise run tf-apply            # AWS 側に CA 鍵 + 署名鍵を作成

CA 用と署名用、それぞれ ECC_NIST_P256(NIST P-256 楕円曲線、COSE/JOSE では ES256 と呼ばれる)の鍵 + alias を 1 つずつ立てます。Terraform 定義は terraform/ ディレクトリ配下を参照してください。

3. 証明書を発行

mise run certs

out/ca.pem(自己署名 Root CA)、out/kms-signer.pem(EE)、out/chain.pem(チェーン)が出力されます。

通常の OpenSSL による証明書発行運用では、openssl x509 -req コマンドがローカルの秘密鍵を用いて一括署名を行います。しかし本構成では、CA の秘密鍵が AWS KMS 内に厳格に封じ込められており、ローカル環境へ取り出すことはできません。そのため、Python スクリプトを用いて以下の手順で段階的に証明書を合成・発行しています。

  1. 証明書の中身(subject / issuer / 有効期限 / 公開鍵 / 拡張)を ASN.1 構造として組み立てて、署名対象部分(TBSCertificate)の DER バイト列を作る
  2. そのバイト列を AWS KMS の Sign API に送る
  3. KMS が返した署名値を受け取る
  4. 中身(1)+ アルゴリズム指定 + 署名値(3)を連結した X.509 証明書として再合成する

ここで出てくる用語を簡単に補足しておきます。

  • ASN.1(Abstract Syntax Notation One): 言語に依存しない形でデータ構造を記述するための国際標準の表記法。X.509 証明書、PKCS、LDAP、SNMP など、暗号・通信プロトコル系の標準仕様で広く使われている
  • DER(Distinguished Encoding Rules): ASN.1 で記述したデータ構造を一意なバイト列に変換するためのバイナリエンコーディング規格。X.509 証明書は最終的に DER バイト列として保存・伝送される(PEM はその DER を Base64 で包んで -----BEGIN CERTIFICATE----- 等のヘッダを付けただけのテキスト形式)
  • TBSCertificate(To-Be-Signed Certificate): X.509 証明書のうち「署名対象になるブロック」のこと。subject / issuer / 有効期限 / 公開鍵 / 拡張など、証明書の中身がここに入っており、CA はこの部分のハッシュに署名する

スクリプト本体は scripts/issue-certs.py を参照してください。

4. 署名対象の画像を用意して署名

c2pa-rs リポジトリのテスト用 fixture(NASA アポロ 17 号の地球画像)をそのまま入力に使います。

curl -L -o input.jpg \
  https://raw.githubusercontent.com/contentauth/c2pa-rs/main/cli/tests/fixtures/earth_apollo17.jpg
mise run sign --input input.jpg

out/signed.jpg が生成されます。c2patool が外部プロセスとして scripts/kms-signer.py を起動し、標準入力(stdin)経由で署名対象ダイジェストを渡し、スクリプトが KMS を呼び出して得た署名値を標準出力(stdout)経由で c2patool に返すことで署名が完了します。署名アルゴリズムや証明書チェーンの指定は CLI ではなく manifest/manifest.jsonalg / sign_cert フィールドで行います。

この段階で検証ログを確認すると、validation_state: Valid かつ signingCredential.untrusted と出力されます。これは暗号署名やダイジェストの構造的整合性は確認できたものの、署名元の CA 証明書が検証側の信頼リストに含まれていないためです。

5. 検証

mise run verify

verify タスクは c2patool out/signed.jpg trust --trust_anchors out/ca.pem で自前 Root CA をトラストアンカーに登録します。c2patool 0.26.50 ではトラストアンカーの指定が trust サブコマンド配下で、フラグ名は --trust_anchors(アンダースコア)です。

これにより検証状態が validation_state: Trusted に更新され、signing certificate trusted, found in System trust anchors と出力されます。このように、Step 4 の「暗号的には正しいが未検証(Valid)」な状態から、Step 5 の「信頼アンカーによって正統性が保証された(Trusted)」状態への遷移こそが、C2PA における信頼モデルの実践的な本質です。

6. 後片付け

mise run tf-destroy

なお仕様上、KMS 鍵は即時削除できないのでご注意ください(最低 7 日の PendingDeletion 期間が入ります)。

ローカル鍵運用との違い

前回記事のローカル鍵運用と、本記事の AWS KMS 運用を整理すると以下の通りです。

観点ローカル鍵AWS KMS
CA 秘密鍵の所在ローカルファイル(ca.keyKMS 内部
署名鍵の所在ローカルファイルKMS 内部
秘密鍵をエクスポートする APIcat 一発提供されない
署名権限の制御ファイル読み取り権限次第IAM ポリシー
監査ログなしCloudTrail に署名ごとに残る
紛失・故障バックアップ次第概念上発生しない

実装上の差分は「ローカルの秘密鍵で署名する処理」が「KMS API を呼び出す処理」に置き換わったのみであり、c2patool 側の外部署名インターフェース(--signer-path)の仕様はそのまま維持されます。さらに、CA 秘密鍵を含めてローカルファイルシステム上から秘密鍵を完全に排除できるため、端末の盗難、バックアップの漏洩、誤ったログ出力などに起因する鍵流出インシデントの発生経路を根本から排除できます。

おわりに

本記事では、c2patool の external signer 機構を活用して AWS KMS へ署名処理を委譲し、Root CA の秘密鍵をも KMS 内に保持させるセキュアな構成を構築しました。非対称鍵の生成から証明書発行、署名、検証という一連のライフサイクルをクラウド KMS 経由で完結できることが確認できれば、Google Cloud KMS や Azure Key Vault への移行や、PKCS#11 を介したオンプレミス HSM / ハードウェアトークンへの接続も、外部署名フック内の実装を差し替えるだけで同様に実現可能です。

c2pa-rs および c2patool にプラグイン型の署名抽象化レイヤーが備わっていることで、署名鍵の「保管場所(セキュリティ境界)」と「利用ロジック(C2PA 処理系)」を明確に分離できます。ローカルファイルからクラウド KMS、専用 HSM、さらにはエッジデバイスの Secure Element に至るまで、サービスの運用要件やセキュリティ基準に応じて柔軟にスケールできる点が、C2PA アーキテクチャの極めて優れた強みです。

参考資料