撮った瞬間に「本物のカメラで撮った」証明を写真・動画に埋め込むカメラアプリ(Android)。
証明の形式は業界標準の C2PA(Content Credentials) です。Adobe・Google・Sony・Leica などが採用している同じ規格なので、 contentcredentials.org/verify などの検証ツールで誰でも中身を確かめられます。
証明を確認するページに写真・動画を置くと、ブラウザの中だけで証明を読み取って表示します。 ファイルはどこにも送信されません。検証エンジンは Content Authenticity Initiative の c2pa-web(MIT)を同梱しています。
DCIM/写丸君(音声メモは Music/写丸君)。ふつうのギャラリーアプリからも見えるiPhone 版は、既定ではインターネットに一切接続しません。 時刻証明の機能もありません(写丸君-iOS.md)。 設定の「会社の証明書で署名する」をオンにしたときだけ、1 年に 1 回、証明書を受け取るために通信します (送るのは公開鍵と App Attest の証拠だけ。写真・動画は送りません)。 さらに「会社のサーバーで署名する」もオンにすると、撮るたびに署名データのハッシュ値(32 バイト)を会社のサーバーに送ります。 オフに戻せば、また一切接続しません。 証明を読むときの遠隔取得(別サーバーにあるマニフェスト・OCSP)も切ったうえで、 c2pa に渡す HTTP の口そのものをふさいでいます(Provenance.swift の context)。
以下は Android 版の話です。
写丸君がインターネットに通信するのは、設定でオンにした機能を使うときだけです。 すべて既定はオフで、全部オフなら一切通信しません。
自己署名だと、誰でも同じ名義を名乗れます。運営会社(株式会社カリロエ)の認証局から端末証明書をもらうと、 証明書のつながりが会社のルートに辿り着くかどうかで本物かどうかが決まるようになります。
| 項目 | 内容 |
|---|---|
| いつ | 1 年に 1 回(証明書の期限が近づいたとき)。受け取ったあとの撮影・署名は端末の中だけ |
| 相手 | 株式会社カリロエの認証局 shamaru-ca-joizw6cvaq-an.a.run.app |
| 送るもの | 署名鍵の公開鍵と、その鍵がこの端末の中で作られたという証拠(Android のハードウェア証明) |
| 送らないもの | 写真・動画・サムネイル・位置情報・アカウント情報・利用者を特定する情報 |
| 受け取るもの | この端末だけの証明書。端末や利用者を特定できる情報は入っていない(名義は全端末で同じ、区別が付くのは乱数の通し番号だけ) |
| 失敗したら | 撮影は止めない。 自己署名のまま撮り、24 時間あけて再試行する |
| 確かめ方 | 受け取った証明書が、アプリに同梱した会社のルートで検証できることを確かめてから使う |
ルート証明書と中間証明書は <https://callirrhoe.jp/app/shamaru-kun/ca/> で公開しています。
Android 0.1.10 以降・iPhone 1.1.0 以降。どちらも既定はオフ。
①の証明書はトラストリストに載らないので、よその検証サイトでは「未認識」のままです。 公式に信頼される証明書は端末ごとに配れないため、端末が claim を組み立て、会社のサーバーが署名する形にします (C2PA の言い方では Distributed 実装クラス)。
| 項目 | 内容 |
|---|---|
| いつ | 1 つ署名するたびに 1 回 |
| 相手 | 株式会社カリロエの署名サーバー shamaru-ca-joizw6cvaq-an.a.run.app |
| 送るもの | 署名するデータのハッシュ値 32 バイト、この端末の証明書(①で受け取ったもの)、使い捨ての乱数と時刻、それらへの端末の鍵による署名 |
| 送らないもの | 写真・動画・サムネイル・位置情報・アカウント情報・利用者を特定する情報 |
| 確かめ方 | サーバーは、端末証明書が自社の中間 CA から出ていること・期限内・署名が合うこと・乱数が未使用であることを確かめてから署名する。証明書だけでは署名しない(端末の鍵による所持の証明が要る) |
| 届かなかったら | 撮影は止めない。 その 1 枚は①の証明書で署名し、5 分は再試行しない |
| 前提 | ①の証明書を受け取っていること(自己署名では認証が通らない) |
1 つのファイルに付く署名は 1 つだけです。①と②を重ねません。
| 項目 | 内容 |
|---|---|
| いつ | 上の設定がオンで、写真・動画・音声メモを1つ署名するたびに1回 |
| 相手 | DigiCert のタイムスタンプ局 timestamp.digicert.com(RFC 3161) |
| 送るもの | 署名データのハッシュ値(数十バイト)。RFC 3161 のタイムスタンプ要求そのもの |
| 送らないもの | 写真・動画・サムネイル・位置情報・端末情報・鍵・証明書・利用者を特定する情報 |
| 受け取るもの | 「このハッシュはこの時刻に存在した」という TSA 署名つきの応答。署名の中に埋め込む |
| 相手に見えるもの | 通信元の IP アドレス(インターネット通信の性質上避けられない) |
| 通信できないとき | 時刻証明なしで署名して保存し、その旨を画面に表示する。撮影は止めない |
| 目的 | 撮影日時(端末の時計)とは別に、第三者が保証する時刻を署名に付けるため |
この通信は Android の INTERNET 権限で行います。この権限は確認ダイアログが出ない種類のものなので、 上の設定スイッチが実質的な同意の場所です。
@ を使った偽装・見た目が似た文字のドメイン・数字のアドレス・ 暗号化なしの接続を見つけたら警告する。開けるのは http / https だけAndroid Studio は不要です。Unity 6000.4.3f1 に同梱の OpenJDK 17 / Android SDK / Gradle 8.13 を使います。
tools/gradle.sh assembleDebug
PowerShell なら .\tools\gradle.ps1 assembleDebug。出力は app/build/outputs/apk/debug/app-debug.apk。
local.properties に sdk.dir を書く(この PC 固有。git には入れない)app/libs/c2pa-release.aar は c2pa-android の GitHub Releases から取得した 0.0.10| ファイル | 役割 |
|---|---|
MainActivity.kt | カメラ画面。CameraX で撮影し、一時ファイルに書いてから署名・保存 |
Settings.kt / SettingsActivity.kt | 撮影の設定と設定画面 |
ImageConvert.kt | JPEG を PNG に変換(EXIF の向きを反映)、署名用サムネイル |
LocationTracker.kt | 位置情報(同意制)。カメラ画面を開いている間だけ測位 |
DeviceKey.kt | Android Keystore に署名鍵を作り、自己署名証明書を付ける |
Provenance.kt | マニフェスト(何を記録するか)の組み立て、署名、読み取りと要約 |
MediaStorage.kt | ギャラリー(DCIM/写丸君)への保存と一覧。Android 9 と 10 以降の差を吸収 |
GalleryActivity.kt / DetailActivity.kt | 撮ったものの一覧と、証明の詳細表示 |
MIT License。第三者ソフトウェアの帰属表示は NOTICE.md を参照してください。