Skip to main content
X 上でエンドツーエンド暗号化されたダイレクトメッセージの送受信を行います:鍵をセットアップし、会話を初期化し、メッセージを送信し、受信トラフィックを復号します。 X Chat アプリは 2 つの要素を組み合わせて使います。
前提条件
  • 開発者アカウント と OAuth 2.0 に対応するように構成された App
  • dm.readdm.writetweet.readusers.read スコープを持つユーザーアクセストークン

1. 依存関係のインストール

PyPI のパッケージ名は chatxdk で、chat_xdk としてインポートします。Python 3.10 以上が必要です。
ユーザーの OAuth 2.0 アクセストークンで API クライアントを作成します。

2. 既存の鍵で Chat XDK を初期化する

このステップは既に所有している鍵をロードします——このアイデンティティで初回セットアップを完了済みの場合に使用します。
  • セキュアキーバックアップ: public-key レコードから取得した juicebox_config で SDK を構築し、unlock にパスコードを渡して秘密鍵を復元します(たとえば新しいデバイスで)。
  • 鍵ブロブ: 以前 export_keys でエクスポートしたブロブを import_keys に渡し、登録済みの鍵バージョンも一緒に渡します(Rust と Go ではこの派生を import_keys_with_version / ImportKeysWithVersion と呼びます)。
その後、あなたのユーザー ID とレコードの public_key_version を渡して set_identity(user_id, signing_key_version) を一度呼び出します。これによりセッションのアイデンティティが保存され、以降のすべての encrypt および prepare 呼び出しはこのアイデンティティとして署名するため、呼び出しごとに送信者 ID や署名鍵バージョンを渡す必要はありません。 初回セットアップの場合は? 同じように SDK を構築しつつ unlock/import_keys はスキップし、ステップ 3 に進んで鍵の作成、バックアップ、登録を行います。
サーバーやボットのサンプルでは鍵ブロブ(export_keys / import_keys)がよく使われます。クライアントアプリではセキュアキーバックアップ(パスコードを使う setup / unlock)がよく使われます。両方のパスについては Chat XDK リファレンスを参照してください。
自前の鍵を持ち込みたい場合は? import_keys は Chat XDK の export_keys が生成した不透明なブロブのみを受け付けます——これは完全な鍵状態のバージョン管理された内部シリアライズであり、生の鍵や PEM エンコードされた P-256 鍵ではありません。このブロブを自分で構築することはできません:ステップ 3generate_keypairs を通じて鍵を生成し、一度ブロブをエクスポートして、base64 エンコードして保存してください。手作りしたり改変したブロブはインポートに失敗します。

3. 鍵を作成して登録する(初回セットアップ)

ステップ 2 で既存の鍵をロードした場合は、このステップをスキップしてください。それ以外の場合、新しいアイデンティティの一度きりのセットアップでは次の 3 つを行います。
  1. 鍵ペアを作成するgenerate_keypairs がアイデンティティ鍵ペアと署名鍵ペアを生成します。
  2. 秘密鍵を保管する — パスコードを使う setup はセキュアキーバックアップに書き込みます(クライアント)。または export_keys が安全に保管するための鍵ブロブを返します(サーバーやボット)。
  3. 公開鍵を登録する — 他者があなたに暗号化したり、あなたの署名を検証したりできるように、登録ペイロードを add-public-key エンドポイントに POST します。
最後に、登録の鍵バージョンで set_identity を呼び出し、このセッションが新しいアイデンティティとして署名するようにします。
すぐに実行できる、各バインディング用の一度きりの登録スクリプトが chat-xdk/examples にあります(Python、TypeScript、Go、Rust、C#、Java)。新しいアイデンティティをオンボードするだけであれば、下記のフローを手作業で実装するのではなく、これらを使ってください。
セキュアキーバックアップには強力なパスコードを使用してください。パスコードを失うか、保護されていない鍵ブロブを失うと、過去のメッセージを復号できなくなる可能性があります。

4. 会話鍵をセットアップする

すべての参加者のアイデンティティ公開鍵を渡して prepare_conversation_key_change を呼び出します。送信者のアイデンティティはステップ 2 で設定したセッションから取得されます。この 1 回の呼び出しで新しい会話鍵が生成され、各参加者向けに暗号化され、変更に署名されます。結果を add conversation keys エンドポイント(POST /2/chat/conversations/{id}/keys)に POST してください——ボディには conversation_key_versionconversation_participant_keys(SDK の encrypted_key → API の encrypted_conversation_key)、および action_signatures(必須。これがないと API は呼び出しを拒否します)が必要です。送信に使うため生の会話鍵を保持してください。 レスポンスは正規の会話 ID(data.conversation_id——1:1 の場合はハイフンで連結されたペア、グループの場合は g プレフィックス付きの ID)と、鍵変更の data.sequence_id を返します。以降のリクエストではこの返された ID を使用し、クライアント側で再構築しないでください。同じ呼び出しは後で鍵をローテーションする際にも使えます:既存の会話 ID を prepare_conversation_key_change に渡し、新しい鍵バージョンで POST します。会話鍵が漏えいした疑いがある場合はローテーションしてください——ローテーションは将来のメッセージのみを保護します。以前の鍵バージョンで暗号化されたメッセージは、そのバージョンを持つ誰でも引き続き読めます。
ラップする前に取得した鍵を検証してください。 prepare_conversation_key_change は渡されたどんな公開鍵に対しても新しい会話鍵を暗号化します。取得した各レコードをまず verify_key_binding(identity, signing, signature) でチェックし——public-keys API から得たレコードの public_keysigning_public_keyidentity_public_key_signature の各フィールドを渡してください——差し替えられたアイデンティティ鍵が会話鍵を受け取れないようにします。

5. メッセージを送信する

ステップ 4 の生の会話鍵で暗号化します。SDK がメッセージ ID(UUID)を生成し、それを署名済みイベントに埋め込み、ペイロード上で返します——自分で生成することは決してありません。送信リクエストでは次のようにマップしてください。 API がハイフン付きの会話 ID を必要とする場合(:-)は URL パスでそれを使用します。SDK 自体は柔軟です:encrypt_messageencrypt_reply は、保持している任意の形式で ID を受け付けます——イベントからの A:B、リスティングや URL パスからの A-B(順不同)、または単に受信者のユーザー ID——署名前に正規化します。グループ ID(g プレフィックス付き)はそのまま渡ります。
このフローではステップ 4 で作成したばかりなので、スニペットでは会話鍵を明示的に渡しています。鍵キャッシュがオンで、decrypt_events パスが会話の鍵を検証した後(ステップ 6)は、encrypt_message(conversation_id, text) だけで十分です——SDK が最新の検証済み鍵を自動で埋めます。リトライでは同じ暗号化ペイロードを再送信すべきなので、ID が二度発行されることはありません。

6. 受信と復号

ライブトラフィックには Webhook かアクティビティストリームを使い、履歴には会話 events をページングします。
  • ライブペイロードフィールド:encoded_event、オプションで conversation_key_change_event
  • 履歴:GET /2/chat/conversations/{id}/events — すべてのイベントに meta.conversation_key_events を加えて decrypt_events を使うのが推奨
  • 復号には、SDK が各メッセージを誰が書いたかを検証できるように、送信者の署名鍵が必要です。これらは他の参加者の 公開 鍵です——ステップ 4 で使ったのと同じ public-keys エンドポイントから取得し、フィールドを SigningKeyEntry にマップします(下のスニペットにマッピングが含まれています)
  • 呼び出しごとに署名鍵(および decrypt_event の場合は会話鍵)を渡すか、あるいは 2 つのオプションのセッションストアを一度セットして短い呼び出し形式を使用できます。下のスニペットはストアを使用しています:set_signing_keys(entries) は参加者の鍵を保持し、set_cache_keys(true)(既定でオフ)は各会話の最新の署名検証済み鍵を保持するので、後の呼び出しは鍵引数を省略できます。どちらのスタイルでも検証は同じです
  • JavaScript は camelCase のイベントタイプ(message)を使いますが、他の言語は "Message" と JSON のスネークケースフィールドを使います
サーバーレスまたは複数インスタンス構成の場合は? 署名鍵ストアと鍵キャッシュは SDK インスタンスのメモリ内に存在します。それが機能しない状況——ある呼び出しが復号し、別の呼び出しが送信する——では、代わりに鍵を明示的に渡してください:decrypt_events(events, signing_keys)decrypt_event(event_b64, conversation_keys, signing_keys)、および encrypt メソッドの conversation_key/conversation_key_version オーバーライド。decrypt_events が返す conversation_keys を自分で永続化して渡し直してください。
すべての言語向けの完全なポーリング/返信ボット:chat-xdk/examples

ベストプラクティス

  • 署名鍵ストアを最新に保つ:送信者が新しい鍵バージョンを登録した場合は完全な参加者セットで set_signing_keys を呼び直し、署名検証失敗時にも更新してください
  • ライブ配信を event_uuid で重複排除してください