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

1. 依存関係をインストールする

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

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

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

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

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

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

prepare_conversation_key_change を、すべての参加者の identity 公開鍵とともに呼び出します。送信者 ID はステップ 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 フィールドを渡します)、置き換えられた identity 鍵が会話鍵を受け取れないようにしてください。

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

ステップ 4 の未加工の会話鍵で暗号化します。SDK はメッセージ ID(UUID)を生成し、署名付きイベントに埋め込み、ペイロード上で返します — 自分で生成することはありません。送信リクエストでは、次のようにマップします: API で要求される場合、URL パスではハイフン付きの会話 ID を使用します(:-)。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 またはアクティビティストリーム を使用します。履歴には会話イベントをページングします。
  • ライブペイロードのフィールド: encoded_event、オプションの conversation_key_change_event
  • 履歴: GET /2/chat/conversations/{id}/events — 全イベントに対する decrypt_eventsmeta.conversation_key_events を優先
  • 復号には、SDK が各メッセージの作者を検証できるように、送信者の署名鍵が必要です。これらは他の参加者の公開鍵です — ステップ 4 で使用したのと同じ public-keys エンドポイントから取得し、フィールドを SigningKeyEntry にマップします(以下のスニペットにはマッピングが含まれています)
  • 署名鍵(および decrypt_event については会話鍵)を毎回渡す、2 つのオプションのセッションストアを一度設定して短い呼び出し形式を使用できます。以下のスニペットはストアを使用します: set_signing_keys(entries) は参加者の鍵を保持し、set_cache_keys(true)(デフォルトではオフ)は各会話の最新の署名検証済み鍵を保持するため、後続の呼び出しでは鍵引数を省略できます。両方のスタイルは同じように検証します
  • JavaScript は camelCase のイベントタイプ(message)を使用します;他の言語は JSON で "Message" と snake_case フィールドを使用します
サーバーレスまたはマルチインスタンス? 署名鍵ストアと鍵キャッシュは SDK インスタンスのメモリに存在します。それが合わない場合 — 1 つの呼び出しが復号し、別のものが送信する — 代わりに鍵を明示的に渡してください: decrypt_events(events, signing_keys)decrypt_event(event_b64, conversation_keys, signing_keys)、および暗号化メソッドの conversation_key/conversation_key_version 上書き。decrypt_events が返す conversation_keys を自分で永続化し、次回渡してください。
すべての言語向けの完全なポール&リプライボット: chat-xdk/examples

ベストプラクティス

  • 署名鍵ストアを最新に保つ: 送信者が新しい鍵バージョンを登録したときは、参加者全員を含めて set_signing_keys を再呼び出しし、署名検証の失敗時は更新する
  • event_uuid でライブ配信の重複を排除する