API version 2026-09-21
APIリファレンス製品アプリ組み込み(Runtime API)
顧客へ配布する製品アプリ向けのAPIです。端末のライセンス認証、利用状態の確認、同時利用数の管理、完全オフラインでの利用を組み込めます。
サービス概要
Usakeyは、クラッキングを不可能にするサービス・技術ではありません。
ソフトウェア解析に対して完全な防御は存在しないためです。
Usakeyはソフトウェア利用者の意図せぬライセンス違反を防ぎ、抜け道を通るコストを引き上げ、発生した不正を記録し、遠隔で止められる状態を保つためのサービスです。
意図せぬ違反を防ぐ
期限、端末数、同時利用数の上限を製品アプリが守るため、利用者が気づかないまま契約の範囲を超えて使うことを防げます。
抜け道のコストを上げる
端末ごとの鍵と署名付きの応答で、キーの使い回しや応答の偽装を難しくします。
発生した不正を記録する
端末の認証、設定の変更、上限を超えた利用を記録し、管理画面で確認できます。
遠隔で止められる状態を保つ
一時停止や取消を、製品アプリの次回の定期確認で反映します。
販売元のUsakeyの契約(プラン)が終了したときや、契約組織が利用停止になったときは、配布済みの製品アプリも、次の定期確認から保護対象の機能を使えなくなります。 Runtime APIが 402 BILLING_REQUIRED を返すためです。購入者のライセンスは停止されていないため、製品アプリは「一時的に利用できません。販売元へお問い合わせください」と表示してください(製品アプリ側の判定表)。
このページで分かること
分かること
- 製品アプリが端末を認証し、利用可否を判定するまでの流れ
- 要求に付与する署名の作成方法と、API応答に含まれる項目
- 停止・期限切れ・通信断など、各状態における実装方針
前提
- 管理画面で製品を登録し、ライセンスを発行できること
- 製品アプリ側でEd25519署名とSHA-256の計算ができること
- 製品IDと署名検証用の公開鍵一覧を、管理画面のAPI・SDKから取得できること
最短の進め方
- 認証の仕組みで全体像を把握する
- 最初に用意するもので必要な設定値と鍵を準備する
- 製品アプリ側の判定表で各状態における動作を決定する
自身で実装せず、AIに任せる方法は第0部 クイックスタートにまとめています。
第0部 クイックスタート
AIに任せて始める
UsakeyはAIアシスタント向けのMCPサーバー(https://usakey.jp/mcp)を公開しています。ユーザー登録をして、Claude CodeやCodexにこのMCPを追加し、ブラウザでログインすると、AIがUsakeyのスキル(AI向けの手順書)を読み、製品アプリへの組み込みやStripeでの販売を実装します。ライセンスの発行や一時停止といった日々の管理も、会話で頼めます。仕組みを理解してから自分で実装する場合は、第1部から読み進めてください。
-
MCPを追加する
Claude CodeやCodexのように、コマンドを実行できるAIに、次の指示文を貼り付けます。
https://usakey.jp/mcp をUsakeyのMCPサーバーとして、このAIアシスタントに usakey という名前で追加してください(HTTPS接続)。追加できたら、ブラウザでUsakeyにログインして接続を許可する手順を教えてください。ブラウザが開いたら、私がログインして接続を許可します。まだUsakeyに登録していなければ、その画面から登録して、許可の画面に戻ります。ブラウザでUsakeyの画面が開いたら、ログインして接続を許可します。まだ登録していなくても、その画面から登録して許可に戻れます。自分でコマンドを実行して追加する方法は、MCPの追加の詳しい手順にあります。
-
やりたいことを日本語で頼む
守りたい機能を伝えると、AIがスキルに沿って公式SDKの取り込みから実装まで進め、停止・期限切れ・通信断の表示とテストまで作ります。ライセンスの発行や一時停止など、日々の管理も頼めます(頼めることの一覧)。
Usakeyのスキルを使って、RustとGPUIでシンプルな時計アプリを作ってライセンス保護して。
MCPを追加する(詳しい手順)
UsakeyのMCPサーバーはUsakeyが運用しています。Node.jsやSDKの用意、APIキーの作成は要りません。AIに頼む場合は、上の手順2の指示文を貼り付けます。追加が終わったら、AIの案内に従ってブラウザでログインし、確認画面で許可します。Claude Codeでは /mcp で usakey を選んで「Authenticate」、Codexでは追加の直後にブラウザが開きます。
自分で追加する
指示文を使わずに、コマンドで追加することもできます。
- Claude Code
-
claude mcp add --transport http --scope user usakey https://usakey.jp/mcpClaude Codeの中で
/mcpを開き、usakeyを選んで「Authenticate」を押すとブラウザが開きます。ターミナルからはclaude mcp login usakeyでもログインできます。claude mcp get usakeyで✔ Connectedと表示されれば完了です。 - Codex
-
codex mcp add usakey --url https://usakey.jp/mcp追加するとログインが始まり、ブラウザが開きます。開かない場合は表示されたURLを開くか、
codex mcp login usakeyを実行します。codex mcp listのAuthがOAuthになれば完了です。 - そのほかのAIアシスタント
- Streamable HTTPで接続し、OAuth(ブラウザでのログイン)に対応したMCPクライアントなら、URL
https://usakey.jp/mcpを追加して使えます。動作を確認しているのはClaude Code 2.1とCodex CLI 0.152です。
ブラウザでログインして許可する
ログインしていない場合は、先にUsakeyのログイン画面が開きます(2段階認証を登録していれば確認コードも求めます)。まだ登録していない場合は、その画面から登録すると、許可の画面に戻ります。続く確認画面で、接続する契約組織と、AIに許可する範囲を選びます。
- スキルだけ使う
- 製品アプリへの組み込み手順(スキル)を読むだけです。ライセンスなどの管理操作はしません。
- 参照だけ許可する
- 上に加えて、製品・利用ルール・顧客・ライセンス・監査ログ・利用状況を見られます。
- 日常の運用を許可する
- 上に加えて、顧客の登録、ライセンスの発行・一時停止・再開・取消、端末の認証解除ができます。
- 製品・利用ルール・Webhook通知の設定も許可する
- 上に加えて、製品と利用ルールの作成・変更、Webhook通知先の登録・変更ができます。通知先を登録すると、ライセンスの出来事が外部へ送られます。
管理操作を許可できるのは、契約組織のオーナーと管理者です。管理操作を許可するときは、操作する環境も次の3つから選びます。
- 動作確認用(テスト環境の製品だけを操作します)
- プロダクション用(顧客へ配布した製品のライセンスが変わります)
- 両方(動作確認用とプロダクション用の製品を操作します)
プロダクション用と両方で「日常の運用を許可する」以上を許可するには、許可する人の2段階認証の登録が必要です。この接続専用のAPIキーが作られ、管理画面の「APIキー」に MCP: で始まる名前で表示されます(両方では環境ごとに1つずつ作るため、1つの接続のキーが2つ並びます)。
ユーザー単位ライセンスのツールは、使えるプランでは最初からチェックされています。サインイン設定(顧客ポータルのOIDC接続)のツールは、最初は外れています。既定の選択は、アプリの発行元を確かめられ、あなたの役割で管理操作を許可できる場合は「参照だけ許可する」、それ以外は「スキルだけ使う」です。プランで選べない項目は、確認画面で薄く表示され、次の理由が添えられます。
- この契約組織のプランではプロダクション用の製品を作れないため、「プロダクション用」と「両方」は選べません(個人プラン以上)。
- この契約組織のプランでは、ユーザー単位ライセンスを使えません(個人プラン以上)。
- この契約組織のプランでは、顧客組織と販売店を使えません(チームプラン以上)。
- この契約組織のプランでは、顧客ポータルのサインイン設定(OIDC)を使えません(チームプラン以上)。
「両方」の接続で、AIがどちらの環境のキーで操作するか(environment引数、一覧で使う月間回数など)は、外部システム連携(Management API)の説明にあります。
許可していない操作のツールは、AIに表示されません。契約プランで使えない機能と、製品数などの枠・利用ルールの値の上限・今月の使用数は、接続時にAIへ伝えます。AIは get_plan_limits(月間回数を消費しません)でも確かめられます。範囲を超える依頼は、ツールを呼ぶ前に止めて利用者へ選択肢を示すよう案内しています。
接続は、管理画面のAIアシスタントの接続からいつでも取り消せます。取り消したあとや、ログインの有効期限(最後に使ってから30日)が切れたあとは、AIアシスタントからもう一度ログインします。
MCPでできること
どの接続でもスキルを読めます。管理操作は、確認画面で許可した範囲のものだけがAIに表示されます。
| できること | 依頼の例 | 必要な許可 |
|---|---|---|
| スキルを読んで組み込む(製品アプリへの端末認証、Stripeでの販売) | 「RustとGPUIでシンプルな時計アプリを作ってライセンス保護して」 | スキルだけ使う(すべての接続) |
| 製品・利用ルール・顧客・ライセンス・端末認証の確認 | 「期限が切れたライセンスを一覧して」 | 参照だけ許可する |
| 組み込み用の接続設定(usakey.config.json)、プランの上限と今月の使用数、端末の定期確認の状況、自社Stripe連携の状態の確認 | 「この製品の接続設定を usakey.config.json に保存して」 | 参照だけ許可する |
| 監査ログと利用状況の確認 | 「直近30日の新規端末認証数と、今日の変更履歴を教えて」 | 参照だけ許可する |
| 顧客の登録とライセンスの発行 | 「株式会社サンプルを登録して、標準ルールでライセンスを発行して」 | 日常の運用を許可する |
| ライセンスの一時停止・再開・取消、端末の認証解除 | 「lic_… を支払い遅延の理由で一時停止して」 | 日常の運用を許可する |
| 製品と利用ルールの作成・変更、Webhook通知先の登録・変更 | 「新しい製品を登録して、3台まで使える月額の利用ルールを作って」 | 製品・利用ルール・Webhook通知の設定も許可する |
| 製品のファイルの暗号化(チームプラン以上): 有効化と暗号化用の公開鍵の取得、ファイルの暗号化、製品アプリで開く実装 | 「この製品のファイルの暗号化を有効にして、models/pro.onnx を暗号化して、アプリで開けるようにして」 | 暗号化と実装はスキルだけ(すべての接続)。確認は「参照だけ許可する」、有効にするのは「製品・利用ルール・Webhook通知の設定も許可する」(足りないときは管理画面で有効にします) |
| ユーザー単位ライセンス(販売店・顧客組織・グループ・ライセンスユーザー・割当) | 「営業部グループに田中さんを追加して」 | 確認画面の「ユーザー単位ライセンス(販売店・組織・グループ・ライセンスユーザー)のツールも使う」(使えるプランでは最初からチェック。個人プラン以上、顧客組織と販売店はチーム以上) |
| 顧客ポータルのサインイン設定(OIDC接続)の確認・変更・停止 | 「顧客ポータルのOIDC接続の一覧を見せて」 | 確認画面でサインイン設定のツールを選ぶ(最初は外れています)。変更は「製品・利用ルール・Webhook通知の設定も許可する」のときだけ(チーム以上のプラン) |
MCPからは行わないこと
- 製品の削除、OIDC接続の作成とClient Secretの変更(IdPの秘密情報を会話に残さないため。管理画面で行います)
- オフライン認証ファイルの発行と、ライセンスキーを手元のファイルへ保存すること(管理画面か、APIキーで使うローカル版を使います)
管理ツールを1回呼ぶごとに、Management APIを1回呼び出します。 契約プランの月間回数を1回消費します(get_plan_limits と、入力の誤りなど何も変えずに断られた呼び出しは数えません。スキルを読むだけなら消費しません)。
ライセンスキーは発行時の応答に含まれ、AIとの会話に残ります。会話に出したくないときは、発行を頼むときに reveal_key: false を指定するよう伝えてください。応答からキーが除かれ、管理画面のライセンス詳細で表示できます。変更系の操作は、AIアシスタントの確認画面で内容を確かめてから承認してください。
UsakeyのMCPサーバーは、MCPの最新版(2026-07-28。セッションを持たず、要求ごとに版とクライアントの能力を送る方式)と、2025-03-26〜2025-11-25の初期化(initialize)を行う方式の両方に対応しています。スキルは、MCPのスキル拡張(io.modelcontextprotocol/skills)と、通常のリソース(skill://…)の両方で提供します。詳しくは外部システム連携(Management API)のMCPの説明を参照してください。
スキルでできること(AI開発者用スキル)
このページの第1部・第2部をまとめた、AI向けの手順書です。製品アプリへの組み込み用とStripeでの販売用の2つを1つにまとめて配布しています。MCPを追加していれば、AIがMCPから読むため別に導入する必要はありません。スキルを読んだAIは、既存アプリの保護対象機能を整理し、Usakeyの現在の仕様に沿って実装とテストまで進めます。
製品アプリに組み込む
usakey-auth-integration
- 公式SDKの取り込みと、言語ごとの組み込み
- 固定端末型・同時利用型・完全オフライン型の選択
- 端末内の鍵生成と安全な保存
- どのプランでも上限に十分な余裕を残す定期確認(起動・再試行を含めた1日の回数まで制御)
- 認証の成功と、失敗の種類ごとの表示・機能制御を全パターン実装し、それぞれをテストする
- 利用者が入力するライセンスキーの受け取りと保存
- ファイルの暗号化(チームプラン以上): 同梱するファイルを暗号化し、製品アプリで開く実装とテスト
- 通信断、明示的な拒否、署名不正を切り分けた処理
Stripeで販売する
usakey-stripe-integration
- 購入前の受け取り確認情報の作成と、Payment Link・Checkout Sessionへの受け渡し
- 支払い成功後のライセンスキーの受け取り、安全な保存、受け取り完了の通知
- 支払い失敗・遅延決済・支払いの再試行・解約・全額返金・一部返金・お試しからの切り替え
- 通信の途中で応答を失った場合や、アプリの再起動をまたぐ受け取りの再開
- 上のすべてのパターンのテストと、Stripeのテスト環境での通し確認
ご自身のStripeで代金を受け取り、決済に合わせてライセンスを発行・停止・取消します(購入に連動したライセンスの発行)。事前に、管理画面の自社Stripe連携を登録してください(有料プランが必要です)。
SDKはスキルが導入します
AIに組み込みを任せると、公式SDKはスキルに同梱の scripts/fetch-sdk.sh が利用中の環境から取得・照合し、製品リポジトリの vendor/usakey-sdk/ へ配置します。SDKを別に導入する必要はありません。 MCPからスキルを読んでいる場合、AIはスキルのZIP(usakey-skills.zip)とチェックサムを製品リポジトリの外の一時フォルダへ取得して照合し、一致したときだけ展開します。
取得スクリプトの内容と実行するコマンドを示して承認を求めてから実行し、終わったら一時フォルダを消します。示された内容を確かめて承認してください。ネットワークから取得できない環境では、MCPのリソースから保存したファイルを、MCPが示すSHA-256と照合してから使います。
配置先は、製品リポジトリ直下の vendor/ の中の1つのフォルダに限ります。リポジトリの最上位やホームフォルダ、リポジトリの外、シンボリックリンクは置き換えません。現在のスキルバージョンは 2026.10.11.3、スキルが取り込むSDKバージョンは 2026.10.11.3 です。配置済みのSDKが 2026.09.24.1 以上なら、スキルを更新してもSDKを取り込みなおす必要はありません。スキルの VERSION にSDKのチェックサムが記録されており、一致しない場合は展開しません。
スキルは設計と実装を支援しますが、プロダクション環境での安全性を自動的に保証するものではありません。 実際の製品ID、API URL、最初に信頼する公開鍵、保護する機能名には、対象環境の値を使用してください。デモ用のライセンスキーや固定の秘密鍵をプロダクション環境へ流用しないでください。
スキルだけを導入する(オプション)
MCPを使わずにスキルだけを入れたい場合や、MCPに対応していないAIで使う場合は、2つのスキルをまとめたZIPを配置します。ZIP直下に usakey-auth-integration と usakey-stripe-integration の2つのフォルダがあります。
スキルを配置する
お使いのAIに、次の指示文を貼り付けてください。AIがスキルを取得し、利用可能な場所へ配置します。
https://usakey.jp/downloads/usakey-skills.zip のスキルを使えるようにしてください
ファイルの取得や作成ができるAIで利用できます。会話のみに対応しているAIの場合は、手順をご利用ください。
- ZIPファイルをダウンロードし、チェックサムを確認して展開します。
- ZIP直下の2つのフォルダ(
usakey-auth-integrationとusakey-stripe-integration)を、お使いのAIのスキル配置先へ移動します。同名フォルダが二重にならないようご注意ください。 - 下記の最終パスに
SKILL.mdとVERSIONがあることを確認し、AIを再読み込みしてスキル名を指定します。
ZIPファイルとチェックサムを同じフォルダに保存し、展開前に以下を実行します。
以下はLinux / macOSの例です。WindowsではPowerShellの Get-FileHash -Algorithm SHA256 でsidecar先頭の値と照合してください。
curl -fSLO https://usakey.jp/downloads/usakey-skills.zip
curl -fSLO https://usakey.jp/downloads/usakey-skills.zip.sha256
if command -v sha256sum >/dev/null 2>&1; then
sha256sum -c usakey-skills.zip.sha256
else
shasum -a 256 -c usakey-skills.zip.sha256
fi- Codex
- 最終パスは
~/.codex/skills/usakey-auth-integration/SKILL.mdと~/.codex/skills/usakey-stripe-integration/SKILL.mdです。$usakey-auth-integrationで呼び出し、AIがスキル名を認識したことを確認します。 - Claude Code
- 最終パスは
~/.claude/skills/usakey-auth-integration/SKILL.md(個人用)または.claude/skills/usakey-auth-integration/SKILL.md(プロジェクト用)です。Stripe連携スキルも同じ場所に置きます。/usakey-auth-integrationで呼び出します。 - そのほか
- スキルの自動読み込みに対応していないAIでは、ZIP内のMarkdownを会話に添付するか、リポジトリに配置して読み込ませてください。
Codexへ配置した後は、必要なファイルが配置先フォルダにあることをコマンドでも確認できます。
test -f ~/.codex/skills/usakey-auth-integration/SKILL.md && \
test -f ~/.codex/skills/usakey-auth-integration/VERSION && \
test -f ~/.codex/skills/usakey-stripe-integration/SKILL.mdZIPで配置した場合、SDKの取得スクリプトは製品リポジトリの最上位フォルダで、配置したスキルのフォルダから絶対パスで実行します。配布元は環境ごとに異なるため、引数で渡します。
cd /path/to/your-product
# Codexへ個人用に配置した場合
~/.codex/skills/usakey-auth-integration/scripts/fetch-sdk.sh https://usakey.jp
# Claude Codeへ個人用に配置した場合
~/.claude/skills/usakey-auth-integration/scripts/fetch-sdk.sh https://usakey.jp更新時は、既存フォルダへ上書き展開せず、新しいZIPの同名フォルダと入れ替えてください。スキルのバージョンは VERSION の USAKEY_SKILL_VERSION で確認できます。SDKは、配置済みのバージョンが同じファイルの USAKEY_SDK_MIN_VERSION 以上なら取り込みなおす必要はありません。ZIP内の各スキルの README.md にも同様の手順が記載されており、チェックサムは /downloads/usakey-skills.zip.sha256 で確認できます。
APIキーで動かしたい場合(CIなどブラウザを開けない環境)や、オフライン認証ファイルの発行・キーのファイル保存をAIに任せたい場合は、公式SDKに同梱のAPIキーで使うローカル版MCPサーバーも使えます。
第1部 認証の考え方
認証の仕組み
Usakeyの認証は、「有効な契約があるか」と「登録した端末からの通信か」を確認し、その結果を製品アプリへ返す仕組みです。利用者が毎回ログインする方式ではありません。
管理者
ライセンスを発行
製品・有効期限・端末数・利用可能な機能を設定し、ライセンスキーを利用者へ渡します。
利用者と製品アプリ
キーを入力し、端末鍵を作成
製品アプリが端末内で2種類の鍵を生成し、公開鍵のみをUsakeyへ送信します。
Usakey
契約と端末を確認
キーの有効性、対象製品、端末数、端末からの署名が正しいかを判定します。
製品アプリ
署名付き利用証明を検証
正規の応答であることを確認してから、ライセンス対象の機能を利用可能にします。
押さえるのは3つ
ライセンスキー・端末の鍵・Usakeyの署名
ライセンスキーで利用を申し込み、端末のみが保持する秘密鍵で「登録した端末からの通信であること」を示します。Usakeyは判定結果に署名し、製品アプリはその署名を確認してから機能を有効にします。
- ライセンスキー
- 管理者が発行し、利用者へ渡す文字列です。初回の端末認証にのみ使用します。
- 端末の鍵
- 製品アプリが端末内で生成する鍵ペアです。秘密鍵は端末外に出さず、公開鍵のみを送信します。
- Usakeyの署名
- 応答と証明書に付与される署名です。偽造や、通信途中での改ざんを検知できます。
登場人物と役割
| 登場人物 | 役割 | 保持するもの |
|---|---|---|
| 管理者 | 契約条件を設定し、ライセンスの発行・停止・再開・取り消しを行います。 | 管理画面のアカウント |
| 利用者 | 初回起動時に、管理者から受け取ったライセンスキーを製品アプリへ入力します。 | ライセンスキー |
| 製品アプリ | 端末鍵を生成し、ライセンス状態を確認し、その結果に応じて対象機能を切り替えます。 | 製品ID、端末の秘密鍵、確認済み証明書 |
| Usakey | 契約・端末数・同時利用数を判定し、署名付きの結果を返します。 | ライセンス情報、登録端末の公開鍵 |
製品署名鍵の準備とプランごとの違い
Usakeyは、応答と利用証明を製品ごとに分離した鍵で署名します。署名鍵は初回のライセンス発行など、実際に必要になったときに自動で準備します。製品を登録した時点で鍵が未準備でも、製品IDや利用ルールを先に設定できます。
- 検証プラン
- 共有の発行用鍵が、用途と有効期限を限定して製品別の署名鍵を承認します。製品別の秘密鍵は必要時に生成して暗号化保管し、別の製品には使いません。期限が近づくと、自動で新しい鍵へ切り替えます。
- 有料プラン
- テスト製品とプロダクション製品のどちらにも、製品専用の取り出せない鍵を必要時に準備します。秘密鍵をUsakeyのアプリケーションへ取り出しません。
検証プランから有料プランへ変更しても、製品アプリの設定変更は不要です。 既存の製品ID、API URL、最初に信頼する公開鍵、およびTrust Bundleの更新番号を維持したまま、次回利用前に製品専用の取り出せない鍵を準備します。
PRODUCT_SIGNING_KEY_PENDING は再試行可能です。 初回利用、検証プランの署名鍵の有効期限切れ後、または検証プランから有料プランへの変更直後に返る場合があります。Management APIでライセンスを発行したときのHTTP 409は、数分待ち、同じリクエスト本文と同じ Idempotency-Key で再試行してください。オフライン利用ファイルの発行では冪等キーを使わず、同じ要求ファイルと同じ指定内容で再試行します。Runtime APIのHTTP 503では Retry-After に従い、公式SDKの一時障害として扱ってください。製品を作り直したり、SDKの設定を変更したりする必要はありません。
初回起動の流れ
初回起動では、ライセンスとその端末を紐付けます。本ページではこの登録を「端末のライセンス認証」と呼び、APIのURLや項目名では activation と表記します。ライセンスキーのみを知る別の端末が登録済み端末になりすますことを防ぐため、端末の鍵も同時に確認します。
-
管理者 → 利用者
ライセンスキーを渡す
管理画面で発行した USK-… を、安全な方法で利用者へ送付します。
-
製品アプリ(端末内)
2種類の鍵ペアを生成する
署名用の鍵とデータ受取用の鍵を生成します。いずれの秘密鍵も端末外へは送信しません。
-
製品アプリ → Usakey
登録を申請する
ライセンスキー、製品ID、2つの公開鍵、一度だけ使用する乱数を送信します。申請内容には端末の署名を付与します。
-
Usakey
契約と端末の正当性を確認する
キーが有効か、対象製品が一致しているか、端末の署名が正しいか、登録可能な端末数を超えていないかを判定します。
-
Usakey → 製品アプリ
署名付きの利用証明を返却する
端末認証ID(activation_id)と、状態・有効期限・利用条件を含む証明書を返却します。
-
製品アプリ
正当性を確認してから機能を有効にする
応答と証明書の署名、製品ID、端末との紐付けを検証します。検証前の情報は利用判定に使用しません。
ライセンスキーを送信するのは、初回の端末認証のときだけです。 以降の通信では、端末認証IDと端末の秘密鍵による署名を使用します。秘密鍵をサーバーへ送信することはありません。
利用中の定期確認
製品アプリは、Usakeyが指定した間隔で「この端末で引き続き利用可能か」を確認します。本ページではこの通信を「定期確認」と呼び、APIの項目名では heartbeat と表記します。
製品アプリ
端末の署名を付与して定期確認
端末認証ID、現在時刻、一度だけ使用する乱数、送信本文を用いて要求に署名します。
Usakey
現在の契約条件を再判定
一時停止・取消・有効期限・利用可能な機能・次回確認時刻を判定します。
製品アプリ
署名を確認して機能へ反映
利用可能な場合は継続し、利用不可の場合は理由を表示して対象機能のみを無効にします。
応答には現在の状態、最新の証明書、次回確認までの秒数が含まれます。同時利用数を制御するライセンスでは、別途「現在の1枠」を示す有効期間の短い利用許可も取得・更新します。項目の一覧は APIから返却されるデータ に記載されています。
毎秒の定期確認は行わないでください。 応答のnext_heartbeat_inまたはheartbeat_inで指定された秒数待機してください。プロトコル上は最短10秒ですが、実際に使える最短間隔はプランごとに長く、1日の回数にも上限があります(下表)。HTTP 429が返された場合はRetry-After秒待機し、再試行時には新しいnonceと署名を生成してください。
| プラン | 端末状態の最短確認間隔 | 通常の確認回数(料金表の1日◯回) | 429で拒否される上限(再試行を含む) |
|---|---|---|---|
| 検証 | 15分 | 1日96回 | 4回/分・16回/時・384回/日 |
| 個人 | 6時間 | 1日4回 | 4回/分・8回/時・16回/日 |
| チーム | 3時間 | 1日8回 | 4回/分・12回/時・32回/日 |
| 大規模 | 1時間 | 1日24回 | 4回/分・24回/時・96回/日 |
上限は、通信に失敗したときの再試行に備えて、通常の回数の4倍まで許しています。製品アプリの設計は、通常の確認回数(料金表の回数)で行ってください。
同時利用枠は大規模プランでのみ利用可能です。枠の更新は最短30分間隔で、1枠につき4回/分・12回/時・96回/日です。取得および返却は1枠につき6回/分・20回/時・100回/日です。
コマンド実行のたびに終了するCLIツールは、起動ごとの定期確認により上記の上限へすぐに到達します。 端末認証ではプロセス内でのみ検証済み証明書を保持できるため、1コマンド=1プロセスの設計では操作のたびに定期確認が実行されます。CLIでは、対話シェルや常駐プロセスとして1度の確認で検証済みの状態を保持し、各操作の直前にはSDKのdecisionのみを呼び出す構成にしてください。429が返された場合は Retry-After を優先し、上限を設けて待機した上で再試行してください。
通信できない場合の対応
通信に失敗しただけでは、ライセンスが停止されたとは判断できません。製品アプリは、最後に正常に検証した証明書と、管理者が設定した通信断ルールを用いて、一時的に判定します。
定期確認の結果
サーバーから明確な応答が得られたか
利用不可の応答
対象機能を無効化する
一時停止・取消・有効期限切れ・署名不正は、通信猶予によって上書きされません。
接続できない
設定した範囲内でのみ継続
最後に検証した証明書が有効な期間に限り、通信断ルールに従います。
利用可能の応答
利用を継続
新しい証明書と、次回確認までの秒数を保存します。
| 確認結果 | 製品アプリの動作 |
|---|---|
署名済みの「一時停止」、取消・期限切れの拒否(403 LICENSE_REVOKED / LICENSE_EXPIRED) | 通信障害ではないため、対象機能を無効化して理由を表示します。 |
| 署名が不正、または製品や端末が一致しない | 通信猶予は適用せず、対象機能を無効化してセキュリティエラーとして処理します。 |
| サーバーへ接続できない、または一時的なサーバーエラー | 最後に検証した証明書が有効な間は利用を継続し、その後は「停止・一定時間のみ継続・警告付きで継続」の設定に従います。 |
契約期限および同時利用枠の有効期限は、通信猶予よりも優先されます。詳細な分岐条件は 製品アプリ側の判定表 にまとめています。
export_verified_state、Node.jsは exportVerifiedState、C ABIは usakey_client_export_verified_state)、巻き戻せない保存先(下の StateStore など)に保存しておくと、再起動後に新しいクライアントで最初に読み込めます(import_verified_state)。読み込むたびに署名から検証し直し、通信できない間も、最後の証明書の期限と通信断の猶予の範囲で判定します(定期確認が成功するまでは通信断として扱います)。取消や端末認証の解除のあとに書き出したもの、時計を5分以上戻したときは読み込めません。保存していない場合は、保存しておいた端末認証IDで登録を復元し、新しい定期確認の応答を検証できるまで保護対象の機能を利用できません。その場合、起動時に通信できなければ、オンラインでの確認が必要なことを表示し、接続の回復を待って再確認します。APIによる明確な拒否や署名の不正を検知した場合は、製品アプリ側でその状態を保存し、以前の利用許可へ戻さないでください。完全オフラインの端末は、下の方法で発行した利用証明ファイルを使います。常時オフラインの端末
閉域網など、はじめからUsakeyへ接続できない端末では、オンラインでの定期確認を行いません。対象端末、管理者、対象端末の順に、署名付きファイルを受け渡します。
01 / 対象端末
利用申請
.usakeyreqを作成する公開鍵と端末の署名を含めます。ライセンスキーと秘密鍵は含めません。
02 / 管理者
管理画面で
.usakeylicを発行する対象ライセンスに申請端末を関連付け、有効期限付きの署名済み利用証明を作成します。
03 / 対象端末
.usakeylicを読み込む署名・製品・端末・有効期限を検証して保存し、期限内のみ対象機能を有効化します。
完全オフライン環境では、管理者による変更を即座に反映できません。 停止などの操作を早く反映させたい場合は、有効期限を短くして発行し、定期的に .usakeylic を差し替えてください。具体的なコマンドやファイル検証の手順は 完全オフライン端末の手順 をご参照ください。
コピーと改ざんの検知
| 検証項目 | 確認方法 | 防止できるリスク |
|---|---|---|
| 登録端末からの要求か | 端末の秘密鍵による署名を、登録済みの公開鍵で検証 | ライセンスキーのみをコピーした別端末からの要求 |
| 過去の要求の再利用ではないか | 時刻と、要求ごとに生成される一度限りの乱数を検証 | 過去の正常な通信をそのまま再送する不正操作 |
| Usakeyからの正規の応答か | 製品に組み込まれた信頼の起点に基づき、応答と証明書の署名を検証 | 偽サーバーによって作成された不正な利用許可 |
| 応答本文が改ざんされていないか | 本文のハッシュ値、製品ID、要求の乱数、HTTP状態を署名対象と照合 | 通信途中や保存後における、状態・有効期限・利用条件の改ざん |
| 別端末への証明書コピーではないか | 証明書内の製品・端末鍵・端末認証IDとの関連付けを確認 | 署名済み証明書を、別の製品や端末へコピーして不正利用する操作 |
署名は、端末自体が盗難に遭い秘密鍵まで不正利用された場合を自動的に検知できるものではありません。プロダクション環境の製品では、端末の秘密鍵をOSの資格情報保管領域やTPMへ保存してください。検証の順序と署名対象の入力データは 応答の検証順序 に記載しています。
管理者の操作が反映されるまで
| 管理者の操作 | オンライン端末への反映 | 完全オフライン端末への反映 |
|---|---|---|
| 一時停止・再開・取消 | 次回の定期確認の成功時に新しい状態を取得し、製品アプリの表示や機能へ反映します。 | 自動では反映されません。現在の .usakeylic の有効期限、または更新ファイルの持ち込み時に反映されます。 |
| 契約期限の到来 | 証明書に記録された有効期限でも判定するため、通信できない場合でも期限を超えて有効になることはありません。 | .usakeylic 内の有効期限で判定します。 |
| 端末認証の強制解除 | 次回の署名付き要求が解除済みとして拒否されます。製品アプリは再認証を案内します。 | 自動では反映されません。発行済みファイルの有効期限を短く保つ運用により、影響期間を限定します。 |
| 利用できる機能の変更 | 次回の定期確認で新しい証明書を取得し、製品アプリが利用条件を再評価します。 | 新しい .usakeylic を発行して持ち込みます。 |
そのため製品アプリは、「起動時に一度だけ確認する」のではなく、画面を開いたままでも状態を再評価できる設計にします。利用不可となった場合もアプリ全体を強制終了させず、設定・状態表示・再認証への導線を残した上で、ライセンス対象の機能のみを停止することを推奨します。
以上で全体像の説明は終了です
第2部は、最初に用意するもの → APIから返却されるデータ → 製品アプリ側の判定表 の順に読み進めることで、スムーズに実装へ進められます。
第2部 実装リファレンス
公式SDKを使う
以降の通信仕様を独自に実装する前に、公式SDKが対応しているかご確認ください。SDKには署名検証、正規化、公開鍵一覧の検証、利用証明の検証、同時利用枠の管理が実装されています。暗号処理を独自に実装しないでください。
対応言語
本体はRustで実装されています。C / C++、Python、Ruby、Node.js / TypeScript、Go、Java / Kotlin / Scala、C# / .NET、PHP、Flutter / Dart、Swift、React Nativeは同じ本体を経由で呼び出し、iOS / Android向けのは同じ本体を経由で呼び出します。
言語・OSごとの動作確認の範囲と、保証の対象外となる組み合わせは、ZIP内の README.md と docs/support-matrix.md に記載しています。
Rust
本体実装。他言語からは本モジュールを呼び出します
rust/usakey/
C
公開ヘッダー(ffi/usakey_ffi/include/usakey.h)を使う例とMakefile
c/
C++
ヘッダーだけで使えるRAIIラッパー
cpp/
Python
ctypes
python/
Ruby
fiddle
ruby/usakey_runtime/
Node.js / TypeScript
koffi
node/
Go
cgo
go/
Java
JNA
java/
Kotlin
Java版を利用(Gradleの設定と例)
kotlin/
Scala
Java版を利用する薄いラッパー
scala/
C# / .NET
P/Invoke
dotnet/
PHP
FFI拡張
php/
Flutter / Dart
dart:ffi
flutter/usakey/
Swift(desktop)
modulemap + C ABI
swift/
React Native
JSI
react_native/
iOS / Android native
UniFFI(別系統)
mobile/
使い方
AI開発者用スキルを導入した場合、SDKはすでに導入されています。 スキルが製品リポジトリの vendor/usakey-sdk/ へ取得・配置するため、以下の手順は不要です。
- ZIPとチェックサムをダウンロードし、照合に成功したことを確認してから、製品リポジトリの
vendor/usakey-sdk/へ展開します。 - C ABI系のbindingを利用する場合は、
ffi/usakey_ffiでC ABIライブラリをビルドします。RustおよびiOS / AndroidのUniFFI bindingでは、この手順は不要です。 - 利用する言語のディレクトリにあるREADMEを参照します。
以下はLinux / macOSでの例です。Windowsでは、PowerShellの Get-FileHash -Algorithm SHA256 でsidecar先頭の値と照合し、Expand-Archive で展開してから、各言語のREADMEに従ってください。
curl -fSLO https://usakey.jp/downloads/usakey-sdk.zip
curl -fSLO https://usakey.jp/downloads/usakey-sdk.zip.sha256
if command -v sha256sum >/dev/null 2>&1; then
sha256sum -c usakey-sdk.zip.sha256
else
shasum -a 256 -c usakey-sdk.zip.sha256
fi
unzip usakey-sdk.zip -d vendor/
cd vendor/usakey-sdk/ffi/usakey_ffi
cargo build --locked --release
case "$(uname -s)" in
Darwin) export USAKEY_LIBRARY_PATH=$PWD/target/release/libusakey_ffi.dylib ;;
Linux) export USAKEY_LIBRARY_PATH=$PWD/target/release/libusakey_ffi.so ;;
esacUSAKEY_LIBRARY_PATH を読むのは、Python、Ruby、Node.js、Java / Kotlin / Scala、C#、PHP、Flutterです。Go(CGO_LDFLAGS。指定しないと target/debug を見ます)、Swift(-Xlinker -L)、C / C++(Makefileの USAKEY_PROFILE=release)、React Native(android/build-ffi.sh)は、ビルドするときに場所を指定します。Windowsでも同じ場所で cargo build --locked --release を実行し、target\\release\\usakey_ffi.dll を指定します(ZIP内の README.md の手順)。macOSとWindowsでのC ABIライブラリのビルドと実行は、まだ確認していません。
展開先には vendor/usakey-sdk/ を推奨します。AI開発者用スキルも同じ場所を前提としており、付属の取得スクリプトがダウンロードと照合を一括して行います。
取り込んだSDKは製品リポジトリに含めて版を固定してください。なお、ビルド生成物(vendor/usakey-sdk/**/target/)は除外してください。ライブラリと各言語のコードは、必ず同一の版から用意してください。起動時にレイアウトの不一致を検出しますが、版の一致を保証するものではありません。
現在のSDKバージョンは 2026.10.11.3 です。ダウンロード後は、usakey-sdk/VERSION および /downloads/usakey-sdk.zip.sha256 のチェックサムをご確認ください。
組み込みがうまく動かないときは、同梱のRust本体の診断(usakey-doctor)で、接続設定ファイル(usakey.config.json)・署名鍵の準備・公開鍵一覧・時計のずれを、定期確認を1回も使わずに確かめられます(cargo run --locked --example usakey-doctor -- --config usakey.config.json。手順はZIP内の README.md)。
Node.jsとPythonには、組み込みのひな形(starters/)を同梱しています。製品のソースへコピーして使うひな形で、SDK本体とは別です。認証、定期確認の間隔と1日の上限回数、状態と端末鍵の保存、日本語・英語の表示文言が含まれています。Linux / macOS向けの常駐エージェントと、SDKをFake Clientに差し替えてすべての判定パターンを試すテストも用意しています。実サーバーでの確認はまだです。使い方と限界は starters/README.md にあります。
販売元が暗号化した同梱ファイル(ファイルの暗号化)を開く関数は、Rust・C ABI・Node.js・Python・RubyのSDKにあります(暗号化したファイルの鍵を受け取る)。ファイルを暗号化するツール(usakey-seal)もRust本体に同梱しています(cargo run --locked --example usakey-seal -- seal --key "$USAKEY_SEALING_KEY" ファイル)。ほかのbindingはまだ開けません。
必ず守ること
- SDKが返す要約情報は表示用です。保護対象の機能ごとに、必ず判定処理を呼び出してください。
- 端末の秘密鍵はSDKへ渡さず、署名のみを返す実装を渡してください(C ABI・Node.js・Python・Rubyでは
DeviceIdentityをそのまま使えます)。 - 署名処理の中から例外を外部へ送出しないでください。SDK側で閉じた判定になります。
- 再起動後は保存した端末認証IDで復元してください。認証し直すと端末枠を1つ使います。
共通ルールの全文は、ZIP内の README.md に記載されています。
動作確認済みの範囲はOSや言語によって異なります。 ZIP内の README.md に、言語ごとの動作確認範囲を記載しています。記載のない組み合わせは保証対象外です。
Rustでそのまま使う場合の条件
- Rust 1.82以降が必要です(crateの
rust-version)。これより古いtoolchainでは解決およびビルドができません。なお、依存crateの解決でedition2024が要求される場合は、MSRVより新しいCargo(例: 1.89)を使用してください。 - Cargoのpackage名は
usakey-sdk、crate名はusakeyです。Cargo.tomlには次のように記述します。
[dependencies]
usakey = { package = "usakey-sdk", path = "vendor/usakey-sdk/rust/usakey", features = ["blocking-session"] }usakey::session(BlockingSession・MachineInfo・take_runtime_error_codeなどの同期セッションAPI)を利用するには、blocking-sessionfeatureが必須です。非同期APIのみを利用する場合は省略可能です。有効なfeature名はrust/usakey/Cargo.tomlの[features]を参照してください。
暗号化保存と巻き戻し検知を確認する
AES-256-GCMは保存内容の読み取りや改ざんを防ぎますが、過去に正しく暗号化されたファイルへの差し替えは、AES-GCM単体では検知できません。状態の世代番号を、状態ファイルと一緒には巻き戻せない場所(TPM、OSのセキュアストレージ、またはサーバー側に記録した最新の世代番号)と照合する必要があります。
Rust SDKを直接使う場合
EncryptedStateStore::with_rollback_guard と StateRollbackGuard を使用します。世代1のファイルを退避し、世代2を保存した後にファイルだけを世代1へ戻すと StateStoreError::RollbackDetected が、現行ファイルの末尾(暗号文)を1バイト変更すると StateStoreError::Authentication が(先頭のヘッダーを変更すると StateStoreError::InvalidEnvelope が)発生し、保護対象機能が有効にならないことを確認します。
C ABI・Node.js・Python・Rubyの場合
StateStore(AES-256-GCMで暗号化し、世代番号の最大値を巻き戻せない場所に置く保存先。古い版への差し替え・改ざん・別の鍵や用途のファイルは拒否します)、Keystore(OSの資格情報ストア、またはアプリの鍵保護を呼び出す fromProtector)、DeviceIdentity(端末鍵を一度だけ生成して専用の保存先に置き、そのまま署名に使える)を使います。C ABIでは usakey_state_store_open・usakey_keystore_open・usakey_device_identity_open などです。検証済みの状態(上の「アプリを再起動したとき」)や受け取りの申込もここに保存します。
そのほかのbinding(Go・Java / Kotlin / Scala・C#・PHP・Flutter・Swift・React Native)とiOS / Android native bindingの場合
これらのは、巻き戻し検知付きの保存先を公開していません。製品アプリ側で、端末認証ID(activation_id)、時刻の記録(clock_snapshot)、これまでに受け取ったの最大の通し番号、判明している取消・端末認証解除・結果不明の状態、アプリ側の世代番号を、製品・端末ごとに改ざんと巻き戻しを検知できる保存先へ保存します。初回起動時以外で、欠落・古い世代・改ざんを検出した場合は、SDKへ復元値を渡す前に保護対象の機能を利用不可にし()、オンラインでの再確認を求めてください。
- Rustでは、巻き戻し検知用の記録を残したまま状態ファイルだけを古い世代へ戻し、
encrypted state rollback detectedが発生して安全側に倒れることを確認します。 - 各bindingではアプリ側のストアへ世代1、世代2の順に保存し、欠落、世代1への差し替え、1バイトの改ざんを個別に再現します。いずれも端末認証の復元前に拒否され、保護対象機能が有効にならないことを確認します。
clock_snapshotと公開鍵一覧の最大の通し番号を同じ方針で保存し、テスト用の時計を5分を超えて戻した再起動試験で、オンラインでの再確認まで保護対象機能が無効になることを確認します。実運用端末の時計は変更せず、テスト用時計または隔離環境を使用してください。
状態ファイルと巻き戻し検知用の記録を同時に戻せる保存先では、再起動をまたぐ巻き戻し検知を確認済みとはみなせません。SDKへ clock_snapshot や最小sequenceを渡せること自体は、保存先が保護されていることを意味するものではありません。
第2部 実装リファレンス
オンライン認証の実装仕様
公式SDKが対象言語に対応している場合、この節は自前実装の手順としてではなく、SDKが満たす仕様の確認用としてご活用ください。初回起動時にライセンスキーと端末の公開鍵を送信し、端末をライセンスへ登録します。登録後は端末の署名を用いて、ライセンスの状態や同時利用枠を定期的に確認します。通信仕様やOpenAPI上では、この一連のAPIをRuntime APIと表記しています。
Godotなどのブラウザ版から認証する
GodotやWASMなどを使ってブラウザ上で動く製品アプリが、CORS制約でUsakeyに直接接続できない場合に設定します。アプリの配信元を登録して有効にすると、ブラウザから端末認証やライセンス確認を直接行えます。
製品の編集画面で「ブラウザからのライセンス認証」を有効にし、配信元を登録してください。MCPのget_product_browser_runtime / update_product_browser_runtime、またはGET / PATCH /mgmt/v1/products/{product_id}/browser-runtimeでも確認・変更できます(products:read / products:write)。初期状態は無効です。HTTPSの配信元を最大20件まで登録でき、テスト製品ではローカル環境のHTTP接続も使えます。
対象は端末認証、定期確認、認証解除、同時利用枠、メール本人確認、試用の開始、時刻確認、署名の検証用情報です。管理API、Console、Portal、暗号化したファイルの鍵の受け取りには適用されません。ブラウザからも端末の署名と応答の署名検証が必要です。
fetchでは credentials: "omit"を指定してください。Cookieによる認証は使いません。GodotのHTTP通信やWASM用のクライアントでも、既存の署名方式と入力形式を使ってください。CORS対応によって、ネイティブ向けSDKがWASMで動くようになるわけではありません。
OPTIONSによる事前確認は、端末登録、同時利用枠、nonce、月間のAPI利用回数、メール送信を消費しません。不正な連打を抑えるため、送信元IPには専用の回数制限があります。
対象製品に許可されていない配信元からの実要求は BROWSER_ORIGIN_NOT_ALLOWEDで拒否します。事前確認が拒否された場合は、ブラウザには通信エラーとして返ります。別製品の設定や、ブラウザに残る事前確認のキャッシュで許可を引き継ぐことはありません。
Godotのスレッドを使うWeb版では、COOPとCOEPをゲームの配信側で設定してください。認証APIへこの2つのヘッダーを付けても、CORSの許可にはなりません。
最初に用意するもの
これらの値は1つのファイルにまとめて取得できます。 管理画面の「開発・連携」→「API・SDK」で、製品ごとに接続設定をダウンロードすると、API URL・製品ID・最初に信頼する公開鍵・権利名を含む usakey.config.json が取得できます。製品のリポジトリに配置してご利用ください。秘密情報(ライセンスキー、APIキー)は含まれないため、そのまま保管できます。AI開発者用スキルもこのファイルをそのまま読み込みます。
- ライセンスキー
- 管理画面でライセンスを発行した直後に一度だけ表示される
USK-...を、顧客へ提供します。製品アプリは利用者の入力またはインストーラから受け取ります。バイナリに共通キーを埋め込まないでください。 - 製品ID
- 管理画面の「開発・連携」→「API・SDK」に表示される
prod_...です。秘密情報ではないため、製品アプリに同梱できます。 - 端末の署名鍵
Ed25519 - 要求が登録済み端末から届いたことを証明するための鍵です。対象端末で鍵ペアを1回生成し、秘密鍵をOSの資格情報保管領域やTPMへ保存します。受信側は公開鍵を使い、「登録した端末から届いたか」と「途中で改ざんされていないか」を確認します。APIへは公開鍵のみを送信し、秘密鍵は送信しません。
- 端末の受取用鍵
KEM鍵 - その端末だけが開ける形式でデータを渡すための鍵です。署名鍵が「誰が送ったか」を、受取用鍵が「誰だけが開けるか」を担当するため、2つに分けています。現在は端末との組み合わせ確認と、将来の暗号化配送に備えて公開鍵を登録します。秘密鍵を端末外へ出すことはありません。
ライセンスキーと端末の公開鍵は、生成する場所が異なります。 ライセンスキーは契約組織の管理者がUsakeyで発行し、端末の鍵は製品アプリが実際に動作する端末上で生成します。
開発用のUsakeyサーバーから取得した接続設定には、「最初に信頼する公開鍵」が含まれないことがあります。 署名用の鍵をクラウドの鍵管理サービスで管理していない開発用サーバーは、応答に自分で署名しています(接続設定ファイルの signing_mode が development_self_signed)。この公開鍵は開発用のもので、製品に組み込んで信頼してよい鍵ではないため、接続設定ファイルの trust_anchor.root_public_key には出力しません。開発中に署名の確認まで試す場合は、その開発用サーバーの管理者から公開鍵のファイルを受け取って使います。製品として配布する設定は、必ずプロダクション環境の管理画面で取得し直してください。
初回の端末認証で付ける署名
device_proof(端末が秘密鍵を持つことを示す署名)を含めていない送信本文を、誰が計算しても同じ並びになる正規化JSON(JCS)へ整形します。その先頭に Usakey-Activation-Proof-v1\n を付与し、端末の署名鍵の秘密鍵で署名します(アプリからの試用の開始の確認では、前置きが Usakey-Trial-Proof-v1\n になります)。署名値はHTTPで安全に扱えるBase64URL形式にします。
POST /runtime/v1/activations
Content-Type: application/json
Idempotency-Key: <リクエストごとのUUID>
{
"license_key": "USK-…",
"product_id": "prod_…",
"device_signing_public_key": "ed25519:…",
"device_kem_public_key": "p256:…",
"device_proof": "…",
"machine_info": {"os": "windows", "arch": "x86_64"},
"nonce": "…"
}一度だけ使う乱数(API項目名: nonce) は、16バイト以上の暗号学的乱数をパディングなしのBase64URL形式にし、新しい論理リクエストごとに生成します。重複実行防止キー(Idempotency-Key) はこれとは別の値です。初回の端末認証を送信した後に結果不明となり、公式SDKが自動再送する間のみ、送信した本文のバイト列そのもの(nonceと署名を含む)と同じ冪等キーを使用します。
アプリからの試用の開始の確認では Idempotency-Key が必須で、再試行では同じキーと同じ本文を送ります。初回の応答から端末認証ID(activation_id)と検証済み証明書を保存し、2種類の秘密鍵も端末の安全な領域で保管します。秘密鍵がAPIから返ることはありません。
登録後の端末署名
ライセンス状態の定期確認や同時利用枠の取得・更新・返却では、次の6行をLF(改行コード \n)で連結し、端末の署名用秘密鍵で署名します。クエリはキーと値の組み合わせをソートしてフォーム形式で再エンコードし、送信本文は署名後に1バイトも変更しません。
Usakey-Request-v1
<UPPERCASE METHOD>
<path?sorted=query>
<Unix時刻(秒)>
<毎回新しい16バイト以上のnonce>
<送信した本文のバイト列のSHA-256(小文字の16進数)>署名対象の実例
次の1行のJSONを、空白や改行を加えずにUTF-8で送信する例です。
{"app_version":"2.0.0","machine_info":{"arch":"x86_64","os":"windows"},"sdk_version":"usakey-rust/0.1.0"}この本文のSHA-256(内容から生成される64桁のハッシュ値)は a63964d27ca2a35ccaca1ad52f18a513ae1d2f806011f93eeeae0125cd27a0c3 です。署名対象は次の6行となります。
Usakey-Request-v1
POST
/runtime/v1/activations/act_0123456789abcdef0123/heartbeat
1787621025
AAECAwQFBgcICQoLDA0ODw
a63964d27ca2a35ccaca1ad52f18a513ae1d2f806011f93eeeae0125cd27a0c3- 時刻
1787621025= 2026-08-25 01:23:45 UTC- 一度だけ使用する乱数
AAECAwQFBgcICQoLDA0ODwは16バイトの説明用固定値です。プロダクション環境では、毎回新しい暗号学的乱数を生成します。- 末尾
- 6行目の末尾には改行を付与しません。
初回認証では、送信可能な項目があらかじめ定義されている machine_info が必須です。追加の権限なしで取得できるOS・CPU・メモリ・ストレージ・実行環境・セキュリティ機能を可能な範囲で送信し、最低限OSとCPU architectureを含めます。
内容に変更があった場合や24時間ごとに完全な情報を送信し、変化のない通常の定期確認では直前の内容のSHA-256のみを送信できます。hostname、machine UUID、machine-id、serial、MAC address、利用者名、home path、環境変数、ファイル・アプリ・プロセス一覧、秘密情報は送信しません。
サーバーが検知した送信元IPとUser-Agentも前回の確認時と比較し、変化は管理画面と操作履歴へ記録します。IPの逆引き名は参考情報であり、端末や利用者の本人確認には使用しません。
SDKがOSから自動収集する項目をアプリ側で上書きすると、実行環境によっては端末認証が構成エラーで失敗します。 例えば model は、WindowsではBIOSの製品名、Linuxでは /sys/class/dmi/id/product_name、macOSでは hw.model から自動収集されます。Rust SDKの MachineInfo.model を設定すると、自動収集済みの項目は上書きできないとして拒否されます(DMIを読めないWSLやコンテナでは通るため、開発機では再現しないことがあります)。アプリのバージョンは専用の app_version オプションで渡してください。app など自動収集されない項目は、Rustの ActivationOptions.machine_info / HeartbeatOptions.machine_info でのみ渡せます(session::MachineInfo、C ABI、各言語のbindingには、この欄がありません)。どの項目が自動収集されるかはOSごとに異なるため、上書きを避けるのが安全です。
端末認証ID、時刻、一度だけ使用する乱数、署名は、それぞれ X-Usakey-Activation、X-Usakey-Timestamp、X-Usakey-Nonce、X-Usakey-Signature の4つのヘッダーに設定します。時刻の許容差は±5分です。
一度使用した乱数は契約組織全体で少なくとも10分間記録し、コピーされたリクエストの再送を拒否します。時刻差エラー(CLOCK_SKEW)の発生時は POST /runtime/v1/time の署名済みサーバー時刻を検証し、ローカルの信頼済み時刻基準を修復します。
APIから返却されるデータ
端末認証、ライセンス状態の定期確認、同時利用枠、時刻確認、メールアドレスでの確認、アプリからの試用の開始の成功応答は、判定結果(data)、照合情報(response_assertion)、短期鍵の委任証明(signing_delegation)、Usakeyの署名(response_signature)の4要素で構成されます。本文のハッシュ値、一度だけ使用する乱数、HTTPステータス、製品ID、鍵の委任と署名を検証するまで、判定結果は使用しません。エラー発生時は下記の error 形式で返却されます。
| 操作 | data の項目 | 製品アプリで保存する項目 |
|---|---|---|
初回の端末認証201 | activation_id, certificate, machine_info_digest | 端末認証ID、検証済み証明書、送信した端末情報のハッシュ値 |
ライセンス状態の定期確認200 | activation_id, license_status, certificate, next_heartbeat_in, machine_info_digest | 新しい証明書、次回確認までの秒数、サーバー保存済み端末情報のハッシュ値 |
同時利用枠の取得201 | lease_id, instance_id, expires_at, heartbeat_in | 同時利用枠IDと有効期限 |
同時利用枠の更新200 | lease_id, instance_id, expires_at, heartbeat_in | 更新された有効期限 |
同時利用枠の返却200 | lease_id, status="released" | ローカルの同時利用枠情報を破棄 |
メールアドレスでの確認の開始202 | verification_id, expires_at, resend_after_sec, code_length | 確認ID(確認コードを送るときに使う)。送り直しまでの秒数は画面の表示に使う |
メールアドレスでの確認200 | named_user_token, expires_at | 保存せず、すぐに端末認証のnamed_user_tokenに渡す |
アプリからの試用の開始202 | verification_id, expires_at, resend_after_sec, code_length | 確認ID(確認コードを送るときに使う) |
試用の確認(発行と端末認証)201 | activation_id, certificate, machine_info_digest, license_id | 初回の端末認証と同じ。ライセンスキーは返らない |
サーバー時刻の確認200 | server_time, sdk_version | 署名検証後の時刻を、ローカル時計補正の信頼基準として保存 |
{
"data": {
"activation_id": "act_...",
"license_status": "active",
"certificate": "v4.public....",
"next_heartbeat_in": 86400,
"machine_info_digest": "<machine_infoのSHA-256>"
},
"response_assertion": {
"v": 1,
"product_id": "prod_...",
"key_id": "key_online_...",
"parent_key_id": "key_prod_...",
"request_id": "...",
"nonce": "...",
"server_time": 1787623200,
"status": 200,
"body_sha256": "<JCS(data)のSHA-256>"
},
"response_signature": "...",
"signing_delegation": {
"delegation": {
"v": 1,
"purpose": "runtime-response",
"product_id": "prod_...",
"parent_key_id": "key_prod_...",
"online_key_id": "key_online_...",
"public_key": "-----BEGIN PUBLIC KEY-----\n...",
"issued_at": 1787620000,
"expires_at": 1787663200
},
"signature": "..."
}
}certificate は、PASETO v4.public形式の署名付き利用証明です。内容は平文のまま閲覧可能ですが、署名によって改ざんを検知できます。暗号化データではありません。 製品ID、端末認証ID、端末鍵のハッシュ値、ライセンス状態、発行・失効・契約期限、通信断時の動作、同時利用数、次回確認間隔、利用可能な機能を検証します。
ユーザー単位ライセンスの利用者をメールアドレスで確かめる
ユーザー単位の利用ルールでは、端末認証(POST /runtime/v1/activations)に、その利用者の認証トークン(named_user_token、発行から5分有効)が要ります。トークンの得方は次の2つで、販売元の契約プランと、利用者の登録のしかたで決まります。
| 方法 | 使える利用者 | 販売元のプラン | 製品アプリがすること |
|---|---|---|---|
| メールアドレスでのライセンス確認 | ローカルIDのライセンスユーザー(販売元が台帳にメールアドレスを登録し、OIDC接続に結び付けていない利用者) | 個人以上 | メールアドレスと、メールで届く確認コードの入力を受け付け、下の手順でトークンを得ます |
| 顧客ポータル | OIDC接続(会社のIDプロバイダー)でサインインする利用者 | チーム以上 | 利用者が顧客ポータルで発行したトークンの入力を受け付けます(利用者管理ガイド) |
トークンを付けずに端末認証すると 401 NAMED_USER_TOKEN_REQUIRED になり、details.available_methods に使える方法が入ります(email はメールアドレスでの確認、portal は顧客ポータル)。検証プランでは []、個人プランでは ["email"]、チーム以上では ["email", "portal"] です。製品アプリはこれを見て、メールアドレスの入力画面と顧客ポータルの案内のどちらを出すかを決めます。
- 利用者にライセンスキーとメールアドレスを入力してもらい、
POST /runtime/v1/email-verificationsにproduct_id・license_key・email・device_signing_public_key(このあと端末認証に使う端末の署名鍵の公開鍵)・nonceを送ります(任意でメールの言語locale)。応答は、アドレスが一致したかどうかにかかわらず同じ202(署名付き)で、verification_id・expires_at・resend_after_sec・code_lengthが入ります。 - メールで届いた6桁の確認コードを入力してもらい、
POST /runtime/v1/email-verifications/{verification_id}/confirmにcode・device_signing_public_key(手順1と同じ鍵)・nonceを送ります。成功すると200でnamed_user_tokenが返ります。 - 利用者が2段階認証(認証アプリ)を登録している場合は
401 TWO_FACTOR_REQUIREDになります。確認コードはまだ有効なので、認証アプリのコード(またはリカバリーコード)をsecond_factor_codeに入れて、確認コードと一緒に送り直します。 - 得たトークンを、端末認証の
named_user_tokenにそのまま入れます(公式SDKでは端末認証のオプションのnamed_user_token)。
- 確認コードを送るのは、次がすべてそろったときだけです。 ライセンスキーが有効、利用ルールがユーザー単位、販売元のプランが個人以上、そのアドレスを持つ有効なローカルIDのライセンスユーザーがちょうど1人(同じアドレスを複数のローカルIDの利用者に登録できません)、その利用者にこのライセンスの割当がある。一致しないときも応答は同じで、製品アプリからは区別できません(登録済みのアドレスを探られないため)。画面には「登録されたアドレスであれば確認コードを送りました」のように表示してください。
- 確認コードは6桁で、10分で期限が切れ、試せるのは5回まで、使えるのは1回だけです。確認コード・端末の鍵・認証アプリのコードの誤りは、それぞれ1回と数えます。同じ端末の鍵で、同じ製品・ライセンスキー・アドレスへ送り直すと、前の確認コードは使えなくなります(別の端末の鍵での開始は、その端末の確認コードを無効にしません)。
- 回数の上限(
429 RATE_LIMITED、Retry-After付き): 同じ接続元IPからの、同じアドレスへの送信は60秒あけ、1時間5回・1日10回まで。同じライセンスキーと同じ接続元IPでは1時間60回・1日300回、同じ接続元IPでは10分30回・1日300回。接続元をまたぐ同じライセンスキー・アドレスへの送信が合計で1時間15回・1日30回を超えたときは、429にせず、確認コードを送らずに同じ202を返します(複数の接続元から同じ利用者へ送り続けられないようにするためです)。確認は接続元IPごとに10分60回・1日600回、認証アプリのコードは利用者ごとに15分5回・1日10回です。送り直しのボタンはresend_after_secを目安にし、429ではRetry-Afterを優先します。 - この2つの要求は、Runtime APIの月間回数に数えません。続く端末認証を1回と数えます。
- 公式SDKでは、Rustの
Client::start_email_verification/confirm_email_verification(Node.jsはstartEmailVerification/confirmEmailVerification、Python・Rubyは同名、C ABIはusakey_client_start_email_verification/usakey_client_confirm_email_verification)が、上の送信と応答の署名の検証を行います。開始から確認までを、端末認証と同じクライアント(同じ端末の署名鍵)で行います。確認の結果は例外ではなく値で返ります(Node.jsの名前。Python・Rubyはcode_invalidのような書き方)。verified(トークン。保存もログへの出力もせず、すぐ端末認証のnamedUserTokenに渡す)codeInvalid・twoFactorInvalid(remainingAttemptsで残りの回数を示す)twoFactorRequired(認証アプリのコードを聞き、確認コードと一緒に送り直す)twoFactorLocked(販売元に解除を頼む)expired(開始からやり直す)assignmentUnavailable(管理者に割当を確かめてもらう)rateLimited(retryAfter秒待つ)
開始の429は例外(
RATE_LIMITEDとretryAfter)で、その待ち時間はresendAfterSecondsより優先します。トークンが要ることは、端末認証の例外のavailableMethods(Python・Rubyはavailable_methods、C ABIはusakey_last_named_user_token_methods())で分かります。この関数があるのはRust・C ABI・Node.js・Python・Rubyだけです。ほかのbindingでは上の手順どおりRuntime APIを直接呼び、応答はほかのRuntime APIと同じ署名付きの形として、
nonceの一致を含めて検証してください(応答の検証順序)。
アプリから試用を始める(メールアドレスの確認)
ライセンスキーを配らずに、製品アプリの中で試用を始められます。利用者がメールアドレスを入力し、届いた確認コードを入れると、販売元が選んだ試用の利用ルールでトライアルのライセンスが発行され、同じ要求の中でその端末が認証されます。利用者にライセンスキーは返しません。
販売元の準備: この製品に、ライセンスキー方式のトライアルの利用ルールを作ります。次に、管理画面の製品の編集にある「アプリからの試用の受付」で、「試用に使う利用ルール」と「24時間に始められる試用の上限」(既定20件、1〜1,000件)を選びます。製品の詳細に、直近24時間に始まった試用の件数が出ます。トライアルを使える個人プラン以上が必要です。
POST /runtime/v1/trial-verificationsにproduct_id・email・device_signing_public_key(このあと使う端末の署名鍵の公開鍵)・nonceを送ります(任意でlocale)。応答はいつも同じ202です。製品が受け付けていないとき(試用の利用ルールが選ばれていない、24時間の上限に達した)も同じ応答で、そのときはメールを送りません。POST /runtime/v1/trial-verifications/{verification_id}/confirmにIdempotency-Key(必須)を付け、code・email(手順1と同じ)と、端末認証と同じ項目(device_signing_public_key(手順1と同じ鍵)・device_kem_public_key・device_proof・machine_info・nonce、任意でmachine_signals・app_version・sdk_version・request_id・client_time)を送ります。license_keyとproduct_idは送りません。device_proofは、前置きUsakey-Trial-Proof-v1\nと、device_proofを除いた本文の正規化JSON(JCS)をつなげたものに、端末の署名鍵で署名します(端末認証の前置きUsakey-Activation-Proof-v1\nとは別です)。- 成功は
201で、端末認証と同じ署名付きのdata(activation_id・certificate・machine_info_digest)にlicense_idが加わります。以後は通常の端末認証と同じく定期確認します。通信の再試行では、同じIdempotency-Keyと同じ本文で送ると、同じ応答が返ります。
- 過去の試用は、確認のときに判定します。 同じアドレスか同じ端末の鍵で、直近365日以内にこの製品を試用していれば
403 TRIAL_ALREADY_USEDです。同じアドレスの試用がまだ有効なら、新しいライセンスを作らず、その試用にこの端末を加えます(2台目・再インストール。新しい試用には数えません)。 - 製品ごとの24時間の上限に達していると、開始ではメールを送らず、確認では
403 TRIAL_ACTIVATION_INVALID(details.reasonがdaily_limit)です。受付を止めた・利用ルールが変わったときはnot_acceptingです。契約組織のトライアル枠がいっぱいなら409 PLAN_QUOTA_EXCEEDEDで、発行と端末認証は取り消し、確認コードは使い終わりになります。 - 確認コードは6桁・10分・5回までです。回数の上限: 同じ接続元IPからの、同じアドレスへの送信は120秒あけ、製品ごとに1時間3回・1日5回、全製品で1日10回まで。同じ接続元IPからの開始は1時間10回・1日30回まで、製品ごとの開始は合計で1時間300回までです(上の上限で断られた開始は、製品ごとの回数に数えません)。接続元をまたぐ同じアドレスへの送信が合計で1時間15回・1日30回を超えたときは、
429にせず、確認コードを送らずに同じ202を返します。確認は接続元IPごとに10分60回・1日600回です。 - 開始はRuntime APIの月間回数に数えません。確認で端末認証まで進んだときに1回と数えます。
- 公式SDKでは、Rustの
Client::start_trial_verification/confirm_trial_verification(Node.jsはstartTrialVerification/confirmTrialVerification、Python・Rubyは同名、C ABIはusakey_client_start_trial_verification/usakey_client_confirm_trial_verification)が、上の手順(前置き・Idempotency-Key・応答の検証を含む)を行い、成功するとそのクライアントが端末認証を終えた状態になります。確認の結果:activated(licenseIdと要約。端末認証IDを保存し、以後は通常の端末認証と同じく定期確認)codeInvalid(残りの回数)expired(開始からやり直す)trialAlreadyUsed(このアドレスかこの端末では試用済み。購入を案内)trialUnavailable(reasonがnotAcceptingかdailyLimit)rateLimited(retryAfter秒待つ)
Rustでは
TrialVerificationOutcome::AlreadyUsed・Unavailableです。端末数の上限(MACHINE_LIMIT_REACHED)や販売元のプランの上限(PLAN_QUOTA_EXCEEDED)は、端末認証と同じ例外です。結果が分からないときは、SDKが同じ本文と同じIdempotency-Keyで1回だけ送り直します。この関数があるのはRust・C ABI・Node.js・Python・Rubyだけです。ほかのbindingでは、上の手順どおりRuntime APIを直接呼びます。
暗号化したファイルの鍵を受け取る(ファイルの暗号化)
販売元が製品に同梱するファイル(学習済みのモデル、設定、データなど)を暗号化し、ライセンスが有効な端末でだけ開けるようにできます。ファイルごとのデータ鍵(AES-256-GCM)は、製品の暗号化用の公開鍵へHPKE(RFC 9180)で包み、ファイルの先頭のヘッダーに入れます。
対応する秘密鍵はUsakeyの外に出ません(プロダクション環境では、取り出せない形で鍵を守る鍵管理サービスの中にあり、環境に1本の鍵を全製品で共用します。製品を分けるのは、包みの関連データに含むヘッダーの製品IDと鍵IDです)。端末はヘッダーだけをUsakeyへ送り、ライセンスの確認のあとで、その端末のP-256方式の受取用鍵あてに包み直したデータ鍵を受け取ります。ファイル本体はUsakeyに送りません。
ライセンスが有効な端末の管理者は、アプリがファイルを開いた後のデータをメモリから取り出せます。暗号化はこれを防ぎません。 見られてはいけない値(APIキーなど)は、暗号化しても端末に置かないでください。ファイルを暗号化しても「解析やクラックは原理的に可能である」ことを理解したうえで使用してください。また暗号化はファイルを作った人を証明しないので、差し替えを防ぎたいファイル(モデルの重みなど)は、アプリで平文のハッシュを確かめてください(マニュアルの例)。
販売元の準備: AIアシスタントに「UsakeyのMCPのスキルを使って、この製品のファイルの暗号化を有効にして、公開鍵を教えて。models/pro.onnxを暗号化して」と頼めます(有効にするには、接続に「製品・利用ルール・Webhook通知の設定も許可する」の許可が要ります。足りないときはAIが管理画面での操作を案内します)。
自分で行うときは、管理画面の製品の詳細にある「ファイルの暗号化」で「ファイルの暗号化を有効にする」を押すか、Management APIの POST /mgmt/v1/products/{product_id}/sealed-files で有効にします(チームプラン以上。有効にした後は無効に戻せません)。
表示される1行の公開鍵(usakey-sealing-key:v1:…。秘密ではありません)を、SDKに同梱の暗号化ツール usakey-seal に渡して暗号化します。--entitlement に機能権限の名前を付けると、その機能権限がオン(true)のライセンスの端末でしか開けません(例:pro_model がオンのプロ版のライセンスだけに高精度のモデルを開かせる)。手順はユーザーマニュアルにあります。
POST /runtime/v1/activations/{activation_id}/sealed-file-keysに、定期確認と同じ登録後の端末署名(X-Usakey-Activation・X-Usakey-Timestamp・X-Usakey-Nonce・X-Usakey-Signature)を付け、本文{"header":"<ヘッダーのバイト列のbase64url>"}(8 KiBまで)を送ります。端末認証の状態は変わりません。- Usakeyは、定期確認と同じ確認に加えて、ライセンスが一時停止中でないこと(定期確認は一時停止中も証明書を返しますが、鍵は渡しません)、販売元のプランにファイルの暗号化が含まれること、ヘッダーが正しい形式でこの製品の使える鍵のものであること、ヘッダーの
entitlementの値がこのライセンスでJSONのtrueであること(値の決め方は証明書と同じ)を確かめます。 - 成功は
200で、署名付きのdataにactivation_id・file_id・header_sha256・enc・wrapped_keyが入ります。wrapped_keyは、端末の受取用の公開鍵あてにHPKEで包み直したデータ鍵です。端末認証・端末の受取用鍵・製品・ヘッダーのSHA-256に結び付いているため、別の端末や別のファイルでは開けません。応答は定期確認と同じ手順で検証します。
- 断られたときのコードは
403 SEALED_FILE_ENTITLEMENT_REQUIRED(details.entitlementに権利名)、422 SEALED_FILE_INVALID、503 SEALED_FILE_KEY_UNAVAILABLE(時間をおいて再試行できる)、403 PLAN_ENTITLEMENT_REQUIRED(details.entitlementがsealed_files)です。どれも端末認証の状態は変えません。扱いは判定表のとおりです。取消・期限切れ・一時停止・解除は、定期確認と同じコードで返ります。 - Runtime APIの月間回数に1回と数え、レート制限は端末認証の操作と同じ枠です。受け取った鍵は端末で保存するので、呼ぶのは同じ端末・同じファイルで初めて開くときと、保存した鍵を失ったときだけです。定期確認の予定には入れません。
- 完全オフラインの端末には、申請ファイルの
sealed_file_headersにヘッダーを入れると、利用証明ファイルで鍵が届きます(完全オフライン端末の手順)。 - 公式SDKでは、Rustの
Client::open_sealed_file/open_sealed_file_pathが、判定の確認、保存した鍵の利用、この要求と応答の検証、復号をまとめて行います。開くたびに、いまの判定(ヘッダーにentitlementがあればその権利名での判定)が利用可のときだけ開き、データ鍵はアプリに返しません。受け取った鍵は検証済みの状態の書き出し(export_verified_state)に含まれるため、再起動のあとも通信せずに開けます。一時停止・取消・期限切れ・解除を知ると、保存した鍵を消します。C ABI・Node.js・Python・Rubyにも同じ関数があります(名前は各言語のREADME)。この関数があるのはこの5つだけです。ほかのbindingでは、ファイルの形式や鍵の受け渡しを自分で実装しないでください。
製品アプリ側の判定表
「アプリ自体を終了する処理」と「ライセンス対象の機能を無効化する処理」は明確に区別してください。次の表の「無効」は、画面や設定を開いた状態を維持しつつ、保護対象の機能だけを無効にするという意味です。
| 状況 | 識別方法 | 動作 |
|---|---|---|
| 有効 | 署名と紐付けが正常で、license_status=active、かつ契約・証明書・必要な同時利用枠がすべて有効期限内 | 保護対象機能を有効化。next_heartbeat_in で次回確認 |
| 一時停止 | 署名済みの定期確認(200)の license_status=suspended。初回の端末認証では 403 LICENSE_SUSPENDED | 即時に無効化し、一時停止中であることと契約管理者への確認を案内します。保存済みの端末認証IDは消さず、通常の予定どおり定期確認を続けます。再開されると、同じ端末認証IDのまま利用可能に戻ります。 |
| 取消 | 403 LICENSE_REVOKED(定期確認・初回の端末認証・同時利用枠の取得と更新)。取消では端末認証も解除されますが、ライセンスの状態を先に返すため ACTIVATION_DEACTIVATED にはなりません | 即時に無効化し、取り消されたことを表示して別のライセンスでの認証を案内します。取消は元に戻らないため、保存済みの端末認証IDを消し、自動の定期確認を止めます。 |
| 契約期限切れ | 証明書の契約期限(license_expiry)への到達、または 403 LICENSE_EXPIRED(期限を過ぎた直後から。定期確認・初回の端末認証・同時利用枠の取得と更新) | 即時に無効化し、契約期限が切れたことと更新手続きを案内します。通信猶予は適用しません。期限切れでは端末認証は解除されないため、保存済みの端末認証IDは消さず、通常の予定どおり定期確認を続けます。販売元が期限を延長して再開すると(Stripeの更新の入金や、お試しから正式ライセンスへの切り替えを含む)、同じ端末認証IDのまま利用可能に戻り、端末枠は増えません。 |
| 端末認証の強制解除 | 410 ACTIVATION_DEACTIVATED。管理者(または顧客ポータルの利用者)が端末の認証を解除したときに、ライセンスが有効・一時停止中の場合だけ返ります(取消・期限切れでは上の行のコード) | 保存済みの端末認証情報を破棄し、ライセンスキーの再入力による再認証を案内します。同じライセンスキーで認証し直せます。自動では再認証しないでください(再認証すると端末枠を1つ使います)。 |
| リスク規則による停止 | 403 ACTIVATION_RISK_BLOCKED / LICENSE_RISK_BLOCKED | 無効化し、販売元への連絡を案内します。自動では再試行しないでください。 |
| 販売元のUsakey契約が有効でない | 402 BILLING_REQUIRED | 保護対象機能を無効化し、「一時的に利用できません。販売元へお問い合わせください」と表示します。購入者に再購入やキーの再入力を求めないでください。 |
| 販売元のプランの機能・上限 | 403 PLAN_ENTITLEMENT_REQUIRED、409 PLAN_QUOTA_EXCEEDED(例: 検証プランのテスト用製品は5台まで。それを超える新しい端末認証で details.quota=device_slots) | 新しい端末では保護対象機能を有効化せず、販売元への連絡を案内します。利用者のライセンスが停止されたとは表示しないでください。認証済みの端末の定期確認は止まりません。 |
| ライセンスキー不正 | 404 LICENSE_NOT_FOUND | 無効である旨を表示して再入力を案内します。自動で別のキーを試行しないでください。 |
| 端末鍵の組み合わせ不一致 | 409 DEVICE_KEY_MISMATCH | 無効である旨を表示し、保存済みの署名鍵と受取用鍵が同じ端末認証の組み合わせか確認します。鍵を更新する場合は、旧端末の認証を解除してから認証し直してください。 |
| 端末上限 | 409 MACHINE_LIMIT_REACHED | 無効である旨を表示し、管理者に旧端末の登録解除または契約変更をご案内ください。同じコードは、そのライセンスがその月(UTC)に新しく登録できる端末の数に達したときにも返ります(端末認証の応答の message で区別できます)。その場合は旧端末を解除しても空かないため、翌月まで待つか販売元へお問い合わせください。 |
| ユーザー単位ライセンスの認証トークン・割当 | 401 NAMED_USER_TOKEN_REQUIRED / NAMED_USER_TOKEN_INVALID、409 NAMED_USER_MISMATCH、403 NAMED_USER_ASSIGNMENT_UNAVAILABLE | 無効化し、利用者の確認をやり直します。401 NAMED_USER_TOKEN_REQUIRED の details.available_methods に email があれば製品アプリのメールアドレスでの確認を、portal があれば顧客ポータルでの認証トークンの発行を案内します(空なら販売元への連絡を案内)。割当が無い場合は、管理者に割当の確認を依頼します。別の利用者のトークンへ自動で切り替えないでください。 |
| メールアドレスでの確認のコード | 401 EMAIL_VERIFICATION_CODE_INVALID(details.remaining_attempts)、410 EMAIL_VERIFICATION_EXPIRED | 契約の停止ではありません。コードの誤りでは残りの回数を示して入力し直してもらいます。期限切れ・使い切り・新しい送信で使えなくなった場合は、メールの送信からやり直します(手順)。 |
| メールアドレスでの確認の2段階認証 | 401 TWO_FACTOR_REQUIRED / TWO_FACTOR_INVALID(details.remaining_attempts)、403 TWO_FACTOR_LOCKED | TWO_FACTOR_REQUIRED では認証アプリのコード(またはリカバリーコード)の入力欄を出し、メールの確認コードと一緒に送り直します(確認コードはまだ有効です)。誤りでは残りの回数を示します。ロック中は、販売元に解除を頼むよう案内します。 |
| トライアル | 403 TRIAL_START_WINDOW_EXPIRED / TRIAL_ALREADY_USED、422 TRIAL_ACTIVATION_INVALID。アプリからの試用の開始の確認では TRIAL_ACTIVATION_INVALID は403で details.reason が not_accepting か daily_limit、契約組織のトライアル枠がいっぱいなら 409 PLAN_QUOTA_EXCEEDED | 無効化し、購入を案内します。アプリからの試用の開始では、TRIAL_ALREADY_USED は「このアドレスかこの端末では試用済み」(別のアドレスでは解決しない)、TRIAL_ACTIVATION_INVALID は「いまは試用を受け付けていない」、PLAN_QUOTA_EXCEEDED は「販売元の試用枠がいっぱい」と表示し、販売元への連絡を案内します(アプリから試用を始める)。 |
| 同時利用上限 | 409 CONCURRENCY_LIMIT_REACHED | 今回の起動では保護対象機能を有効化せず、時間をおいて再取得してください。既存利用者の同時利用枠を奪わないようにします。 |
| 同時利用枠の期限切れ | 410 LEASE_EXPIRED またはローカルの expires_at 到達 | 同時利用型では常に無効化し、新しい同時利用枠を取得できた場合のみ復帰します。 |
| 同時利用枠の操作時にライセンスが使えない | 403 LICENSE_UNAVAILABLE(一時停止中など。取消・期限切れは LICENSE_REVOKED / LICENSE_EXPIRED で返ります) | 無効化し、定期確認で現在の状態を確かめます。 |
| 方式の呼び分けの誤り | 422 OFFLINE_PACKAGE_REQUIRED / LEASE_NOT_SUPPORTED / OFFLINE_UNMANAGED_HEARTBEAT_DISABLED | 実装の誤りとして記録して直します。完全オフライン専用のライセンス(OFFLINE_PACKAGE_REQUIRED)は、利用申請ファイルの手順へ案内します。 |
| 端末情報の送り直し | 422 MACHINE_INFO_REQUIRED / MACHINE_INFO_REFRESH_REQUIRED | 完全なmachine_infoを付けて送り直します(公式SDKは自動で行います)。利用者には表示しません。 |
| 時刻のずれ・同じ要求の再送 | 401 CLOCK_SKEW / NONCE_REPLAYED | 契約の停止ではありません。POST /runtime/v1/time の署名付きサーバー時刻で時刻の基準を直し、新しいnonceと署名で送り直します。 |
| ブラウザの配信元が未許可 | 403 BROWSER_ORIGIN_NOT_ALLOWED | 販売元が製品の編集画面でブラウザからの認証を有効にし、配信元を登録してください。事前確認で拒否された場合、ブラウザには通信エラーとして返ります。 |
| 端末の署名・識別の不一致 | 401 DEVICE_PROOF_INVALID / ACTIVATION_MISMATCH / INSTANCE_MISMATCH | 無効化し、保存した鍵と端末認証IDを確認します。直らなければ再認証を案内します。 |
| 送信内容の誤り | 400 INVALID_REQUEST / 413 REQUEST_TOO_LARGE / 422 CERTIFICATE_PAYLOAD_TOO_LARGE | 実装の誤りとして記録して直します。「停止された」とは表示しないでください。 |
| 返却・解除の対象が無い | 404 NOT_FOUND | 同時利用枠の返却や端末の解除では、完了したものとして扱います。 |
| 暗号化したファイル(ファイルの暗号化) | 403 SEALED_FILE_ENTITLEMENT_REQUIRED / 422 SEALED_FILE_INVALID / 503 SEALED_FILE_KEY_UNAVAILABLE | どれもライセンス認証の状態は変わりません。SEALED_FILE_ENTITLEMENT_REQUIRED はファイルが求める機能権限(details.entitlement)がこのライセンスに無いので、そのファイルを使う機能だけを止めて案内します。SEALED_FILE_INVALID は別の製品向け・壊れた・使えない鍵のファイルなので、再試行せずファイルの入れ直しを案内します。SEALED_FILE_KEY_UNAVAILABLE は時間をおいて再試行します。 |
| 通信断・5xx・429 | 署名済みの新しい応答を受け取れない(応答の前に接続が切れた場合や、本文がUsakeyのエラーJSONでない429・5xx(ロードバランサー・CDNのHTMLの画面、空の本文)を含む)。429 RATE_LIMITED と、503 PRODUCT_SIGNING_KEY_PENDING / NONCE_STORE_UNAVAILABLE / LEASE_STORE_RECOVERING / LEASE_STORE_UNAVAILABLE などの一時的な拒否を含む | 下記の通信断判定に従います。定期確認や同時利用枠の429は Retry-After を優先し、再試行時に現在時刻、新しいnonce、新しい署名を生成します。 |
| 署名不正・製品/端末不一致・乱数不一致 | 応答または証明書の検証失敗 | 通信猶予で上書きせずに無効化し、セキュリティエラーとして記録します。 |
通信断時のローカル判定
- キャッシュ済み証明書の署名、製品ID、端末との紐付け、ローカル状態の改ざん検出を最初に行います。
license_statusがactive以外、またはlicense_expiry到達済みの場合は常に無効とします。- 証明書の
expires_at前なら利用可能です。到達後はfail_mode=closedなら無効、graceならexpires_at + network_grace_secまで警告付きで継続、openなら機能制限中の表示で継続します。 - 同時利用型では
fail_modeに関係なく有効なリースが必須です。利用上限は「リース期限、証明書/通信猶予の上限、契約期限」のうち最も早い時刻となります。
第2部 実装リファレンス
購入に連動したライセンスの発行
ご自身のStripeアカウントをUsakeyに接続すると、購入者の決済に合わせてライセンスの発行・停止・再開・取消を自動で行えます。代金を受け取るのは販売元であるお客様自身であり、Usakeyは決済通知を受けてライセンスを操作します。これはUsakeyのご利用料金(契約プラン)のお支払いとは別の仕組みです。本機能のご利用には有料プランのご契約が必要です。
購入手続きは、購入ごとにいずれか1つを選択します。 新しいライセンスを発行する通常購入は、製品アプリから静的なPayment Linkを開く方法と、販売元のバックエンドでCheckout Sessionを作成する方法のどちらでも実装できます。これら2つの方法は連続する手順ではありません。お試しと同じライセンスを引き継ぐ購入のみ、購入者ごとのライセンスIDを安全に設定する必要があるため、販売元のバックエンドでCheckout Sessionを作成する方法を選択します。
Stripeの資格情報は用途ごとに分け、製品アプリには含めないでください。 Usakeyに保存するのは、通知処理に必要な最小限の権限のみを持つUsakey連携用RAKです。販売元のバックエンドでCheckout Sessionを作成する場合は、環境に応じた別のStripe資格情報を秘密情報保管庫に配置し、作成に必要な操作のみを許可します。その資格情報はUsakeyへ登録しません。Stripe Payment Linksから購入を開始する場合、Checkout Session作成用のバックエンド資格情報は不要です。製品アプリはStripeの資格情報を保持せず、購入前に生成した受け取り確認用の秘密値をUsakeyの受け取り口へ送信します。決済後のCheckout Session IDも取得できた場合は、同一申込であることを照合するために併せて送信します。
管理画面で設定すること
- 初回の動作確認では、Stripeで用途が分かりやすい名前を付けた新しいSandboxを作成します。本番設定のコピーはオフにし、以降のキー、Webhook、商品、価格はすべて同じSandbox内で作成します。
- Stripeの「開発者」→「APIキー」から、Usakey連携専用の制限付きキー(
rk_)を作成します。「サードパーティーへ提供」など多数の権限が最初から有効になる作成方法は使用せず、下記の5つの権限のみを個別に設定します。秘密キー(sk_)や、販売元バックエンド用の資格情報は登録しないでください。 - Usakeyの「開発・連携」→「自社Stripe連携」の登録画面にあらかじめ表示されている通知先URLを、同じStripe SandboxのWebhookへ登録します。表示された署名シークレット(
whsec_)と制限付きキーをUsakeyへ保存します。 - 同じStripe Sandboxで商品と価格を作成し、その価格とUsakeyの製品・ポリシー・期間を対応付けます。本番環境のPrice IDや別のSandboxのPrice IDを混在させないでください。
Usakey連携用RAKには、Checkout Sessions・Subscriptions・Invoices・Eventsの読み取り権限と、Customersの書き込み権限が必要です。Eventsの読み取りは、Usakeyの通知処理を更新した際に、更新前に記録した通知をStripeで再照合する目的のみに使用します。
Customersへの書き込みは、決済後にStripe Customerのmetadataへ usakey_license_id と usakey_license_status を記録し、購入者と発行済みライセンスを照合できるようにする目的のみに使用します。
usakey_license_status は購入を反映した時点の状態で、その後の一時停止・取消では更新しません。氏名、メールアドレス、支払方法は変更しません。このRAKをCheckout Session作成用に流用することはありません。
通知処理を更新したときにEventsの読み取り権限がなく、照合できない全額返金の通知が残った場合は、返金の対象を特定できないため、この連携の現在の種別で購入された利用中・期限切れのライセンスをすべて一時停止し、管理画面に案内を表示します。解決するまで新しい購入のライセンス発行は保留され(一時エラーを返し、Stripeの再送を待ちます)、Stripeの種別も切り替えられません。Stripe側で同じ制限付きキーにEventsの読み取り権限を追加するか、新しい制限付きキーへ更新した上で、管理画面の自社Stripe連携にある「反映待ち・隔離中の通知を再照合」を実行してください。再照合は繰り返し実行でき、返金済みの場合は取り消し、誤って止めた場合は現在の契約状態を確認して再開します。
CheckoutではDynamic Payment Methodsを使用し、payment_method_types を固定しません。サブスクリプションの場合はStripe Billingの継続課金Priceを用意し、Checkout Sessionを mode=subscription で作成します。
Stripe Taxを利用する場合は、automatic_tax[enabled]=true の指定に加えて、納税登録済みの地域をStripeへ登録してください。自動税計算は、登録していない地域での税徴収開始を代替するものではありません。
対象のSandboxを開いた後のStripe Dashboard: APIキー・Webhook・商品カタログ。これらのリンクは、Stripe Dashboardで現在選択されている環境を引き継ぐことがあります。作成や保存を行う前に、画面上部のSandbox表示と対象のSandbox名が一致していること、およびプロダクション環境ではないことを確認してください。異なる場合は、アカウント選択から対象のSandboxへ切り替えてから、リンクを開き直してください。
1つの契約組織に登録できるStripe連携は1件のみです。テスト用からプロダクション用へ移行する場合にのみ既存の連携を編集し、プロダクション用のRAKとWebhook署名シークレットを両方再設定します。切り替え時にテスト用の有効な価格対応はすべて停止されるため、同じ画面からプロダクション用のPrice IDを新しく対応付けてください。
プロダクション用からテスト用へ戻すことはできません。live連携後にSandboxでの検証を続ける場合は、別の契約組織へテスト用連携を登録してください。テスト用連携にプロダクション用の通知が届いた場合は、一時エラーを返して履歴を確定させず、連携をプロダクション用へ修正した後のStripeからの再送で処理できるようにします。逆にプロダクション用連携にテスト用の通知が届いた場合は、ライセンスの操作は行わず、履歴に理由を記録します。
制限付きキー(rk_)の作成はStripe Dashboardの画面上でのみ可能であり、Stripe APIやStripe CLIからは作成できません。対象のSandboxにログインできるブラウザでの手作業が必要です。また、Stripeからの通知が実際に届くか動作確認を行う際は、UsakeyがStripe APIへCheckout Sessionを照会する仕様上、実際のSandboxアカウントが必須となり、モックのWebhookでは検証できません。
localhostで開発する場合のWebhook配送
Stripeから localhost へは直接通知できません。開発環境では stripe listen --forward-to でStripeからのイベントをUsakeyの通知先URLへ転送するか、ngrokなどの公開tunnelを利用します。
stripe listen は起動時に端末や契約ごとに固定の署名シークレット(whsec_)を表示するため、それをUsakeyのWebhook署名シークレットとして登録してください。Usakeyが処理するイベントは以下のとおりです。Stripe DashboardでのWebhook登録や stripe listen --events では、これらを選択してください。
checkout.session.completed
checkout.session.async_payment_succeeded
checkout.session.async_payment_failed
invoice.paid
invoice.payment_failed
customer.subscription.updated
customer.subscription.deleted
charge.refundedstripe listen --forward-to <Usakeyの通知先URL> \
--events checkout.session.completed,checkout.session.async_payment_succeeded,\
checkout.session.async_payment_failed,invoice.paid,invoice.payment_failed,\
customer.subscription.updated,customer.subscription.deleted,\
charge.refundedstripe listen はCheckoutの実行中も起動したままにしてください。CLIのサインイン(stripe login)は、発行されたURLを通常のブラウザで開いて承認する方式です。Webhook転送ではイベントが発生順どおりに届かない場合がありますが、Usakeyが発生順の差異を吸収し、Stripe APIで現在のSubscriptionを再照合します。
製品アプリで実装すること
StripeのWebhookはUsakeyが受信するため、製品アプリ側でWebhookの受信を実装する必要はありません。
公式SDKの ClaimRequest / ClaimClient(Rustは usakey::claim、C ABIは usakey_claim_request_generate・usakey_claim_once・usakey_claim_acknowledge、Node.js・Python・Rubyは同名のクラス)が、秘密値・challenge・Idempotency-Keyの生成、受け取り(結果は delivered・pending・finished・retryLater)、受け取り完了の通知、次に試す時刻の計算(申込を作ってから17日まで)を行います。申込は上の StateStore に保存します。
- 購入画面を開く前に、受け取り確認用の秘密値とIdempotency-Keyを生成して安全に保存します。
- 秘密値のハッシュ値だけをCheckoutに結び付け、秘密値そのものはStripeや販売元へ渡しません。
- 決済後に秘密値を受け取り口へ送信し、キーを安全に保存します。Session IDを取得できた場合は併せて送信し、戻り先への移動が失われた場合は秘密値だけで回復します。遅延決済の結果待ちは再起動後も再開します。
- 保存できたことを確認してから、同じIdempotency-Keyで受け取り完了の通知(ACK)を送信します。
通信中に201応答が失われた場合でも、ACKの前であれば同じIdempotency-Keyで同じキーを再取得できます。別の冪等キーに変更しないでください。
ライセンスキーの受け取り
- 製品アプリで暗号学的乱数32バイトを生成し、パディングなしBase64URL形式にした43文字の
claim_verifierを、購入開始前に安全なローカル領域へ保存します。受け取り用のIdempotency-Keyも別途生成し、challenge、claim verifier、取得できた場合のSession IDとともに、ACK完了まで変更せずに保存します。 base64url(SHA-256(claim_verifierのUTF-8 byte列))を計算し、先頭にusk_claim_v1_を付与したchallengeを生成します。ハッシュ値は秘密ではありませんが、claim_verifier自体はStripe、販売元バックエンド、URLへ渡しません。- 選択した購入手続きに応じて、challengeをどちらか一方に設定します。販売元バックエンドでCheckout Sessionを作成する場合は
metadata[usakey_claim_challenge]、静的なPayment Linkを使用する場合は製品アプリが開くURLの?client_reference_id=usk_claim_v1_...です。 - 販売元バックエンドでCheckout Sessionを作成する場合は
success_urlに、Stripe DashboardでPayment Linkを作成する場合は「決済後」リダイレクトURL(APIではafter_completion.redirect.url)に、それぞれ?session_id={CHECKOUT_SESSION_ID}を含めます。購入者を認証した購入完了ページから製品アプリへ渡します。戻り先への移動が失われてSession IDを取得できなかった場合でも、保存済みのverifierだけで回復できます。
既存のStripe連携は、購入手続きを更新してから一度だけ切り替えます。 製品アプリと、現在利用しているすべての購入手続き(Payment Link、または販売元バックエンドで作成するCheckout Session)について、上記のchallengeを購入開始前に生成してStripeへ渡す手順にあらかじめ更新します。その後、管理画面の自社Stripe連携で「安全な受け取りを有効にする」を1度だけ実行します。切り替え前に開始されたCheckout Sessionは、遅延決済が後から完了した場合でもlegacy手順で受け取れますが、受け取り可能なのはキー発行後72時間までです。切り替え後に開始する購入には claim_verifier が必須であり、欠けているとライセンスを発行しません。この切り替えは元に戻せません。新しく登録したStripe連携は最初から安全な受け取りが必須のため、このボタンは表示されません。
POST /integrations/stripe/{claim_token}/license-claims
Idempotency-Key: <ACK完了まで固定する、英数字・-・_ の16〜128文字>
Content-Type: application/json
{"claim_verifier": "...43文字...", "checkout_session_id": "cs_test_..."}購入前にchallengeを渡した申込では、checkout_session_id は任意です。決済後の戻り先への移動(リダイレクト)が失われた場合でも、端末に保持した43文字の claim_verifier だけで、同じStripe連携内の受け取りを再開できます。
Session IDを取得できた場合は照合用に併せて送信し、両方が同じ申込を指していることを確かめてください。Session IDを指定した場合は両方の一致が必須で、Session IDだけでの検索や、別のverifierへの切り替えは行いません。なお、challengeを使わない切り替え前の旧方式の申込に限り、Session IDが必須です。
claim_token は、管理画面の自社Stripe連携に表示される、受け取り先を識別するための固定のIDであり、製品アプリの環境別設定として保持します。利用者ごとの秘密ではありません。管理画面でWebhook通知先URLを再生成した場合でも、配布済みアプリや遅延決済中の申込を保護するため、claim_token と受け取りURLは変更されません。
成功すると 201 で以下が返ります。
{
"license_id": "lic_...",
"license_key": "USK_TEST-...",
"status": "active",
"expires_at": "2027-03-31T23:59:59+09:00"
}expires_at はUTCからの時差付きのISO 8601で、買い切りでは null です。status は active 以外(suspended など)のこともあります。テスト環境の製品のキーは USK_TEST- で始まり、本番の製品のキーは USK- で始まります。
保存確認前にACKしないでください。 201応答のキーを安全な保存先へ書き込み、一時ファイルのflush / fsyncとatomic rename、またはOSの資格情報保管領域からの成功応答などによって永続化を確認します。Usakeyがキーを発行してから72時間以内かつACK前であれば、201応答を失った場合でも同じ claim_verifier と同じ Idempotency-Key(最初にSession IDも送信した場合は同じSession ID)で再取得できます。保存後は次のACKを送信し、Usakeyに一時保管された暗号文を削除します。
POST /integrations/stripe/{claim_token}/license-claims
Idempotency-Key: <取得時と同じ値>
Content-Type: application/json
{"claim_verifier": "...43文字...", "checkout_session_id": "cs_test_...", "acknowledge": true}ACK成功は 204 No Content です。応答を失った場合は同じ内容でACKを再送でき、処理済みの場合も204になります。有効な201でキーを安全に保存済みであれば、発行後72時間を過ぎた場合や一時保管された暗号文の削除後も、同じIdempotency-KeyによるACKはキーを再表示せず204になります。期限後はキーを再取得できないため、ACK完了まではclaim verifierとIdempotency-Keyを保持してください。
claim verifier自体が受け取り資格です。 43文字のverifierだけで新しい申込のキーを取得できるため、ライセンスキーと同様に漏洩させないでください。Checkout Session IDも、知っている人なら照合に使えてしまう一時的な情報として扱います。購入完了ページでは購入者を認証し、第三者analytics、アクセスログ、エラーログ、ReferrerへSession ID・claim verifier・ライセンスキーを渡さないでください。Session IDをアプリへ引き渡した後は history.replaceState などでURLから直ちに除去します。
応答別の対応
| 応答 | 意味 | 製品アプリの動作 |
|---|---|---|
| 201 | キーを発行しました(ACK待ち) | 安全に保存してから同じIdempotency-KeyでACKを送信します。応答を受信できなかった場合は同じkeyで再取得します |
| 204 | ACKを受理しました | 一時保管データが削除されたため、保存済みのキーで通常の端末認証へ進みます |
| 400 INVALID_REQUEST | Session IDと、形式の正しい43文字のverifierのどちらもない。acknowledge が真偽値(true / false)でない。または、申込が見つかった後に、Idempotency-Keyが無いか、英数字・-・_ の16〜128文字でない | 実装を修正し、秘密値やSession IDをログに出力しないようにします |
| 404 NOT_FOUND | 受け取り先のURL(claim_token)の誤り、販売元が連携を停止中、該当データなし、決済結果の確定待ち、指定したSession IDに対するverifierの欠落・形式不正・不一致、または双方が別々の申込に紐づく状態。ACK時は、201を受け取る前のACK、または形式は正しいもののIdempotency-Keyが不一致 | 取得時は下記の期間、同じ値で再試行します。ACK時は取得時と同じkeyであるかを確認します |
| 413 REQUEST_TOO_LARGE | 送信した本文が大きすぎます(1 MiBまで) | 実装を修正します |
| 429 RATE_LIMITED | 同じ接続元からの要求が多すぎます(1分あたり30回まで) | Retry-After 秒待ち、同じ値で再試行します。再試行をやめないでください |
| 410 ALREADY_CLAIMED | ACK済み、または別のIdempotency-Keyで受け取り済み | 保存済みのキーを使用します。保存できていない場合は販売元へ問い合わせます |
| 410 NOT_REQUIRED | 新しいキーは発行されていません | エラーではありません。後述の「お試しからの切り替え」をご確認ください |
| 410 REVOKED | ライセンスが取消済み(解約・全額返金・販売元による取消) | キーは取得せず、この申込に対する自動再試行を終了します |
| 410 EXPIRED | 受け取りの有効期限切れ(キーの発行から72時間。受け取り済みでも、期限後はこのコードになります) | 販売元へのお問い合わせを案内します |
キー取得時の404に対する再試行を、購入から72時間で打ち切らないでください。 Dynamic Payment Methodsには、決済結果の確定までに最大14日程度かかる決済手段があります。存在しない申込、決済結果の確定待ち、verifierの不一致、およびverifierとSession IDの不一致は、レスポンスから区別できません。購入直後は数分間隔で再試行し、その後は待ち時間を徐々に延ばす低頻度の再試行(間隔の上限あり)へ移行した上で、再起動後も処理を再開します。判定にはcodeを使ってください。messageの文字列では判定しないでください。challenge、claim verifier、Idempotency-Key、および取得できた場合のSession IDは、安全なローカル領域へ少なくとも17日間(結果待ち最大14日程度+キー発行後72時間)、またはACK完了まで保存してください。販売元のバックエンドで決済の失敗が確認できた場合、201のキーを保存してACKが完了した場合、または NOT_REQUIRED / REVOKED / EXPIRED / ALREADY_CLAIMED が返された場合にのみ、該当する申込の自動再試行を終了します。
お試しからの切り替え
お試し中の利用者が購入した場合でも、ライセンスキーは変更されません。 Checkout Sessionの metadata[usakey_license_id] に既存のお試しライセンスのIDを設定しておくと、Usakeyは同一ライセンスの利用ルールと有効期限のみを更新します。client_reference_id は受け取りchallenge用であり、ライセンスIDには使用しません。受け取りエンドポイントは NOT_REQUIRED を返すため、製品アプリは保存済みのキーをそのまま継続して利用してください。
切り替えるのは、IDが購入した価格と同じ製品の、まだ切り替えていないお試しライセンス(利用中または期限切れ)を指すときだけです。 一致しない場合(IDの誤り・別の製品・切り替え済み)は新しいライセンスとキーを発行し、受け取りは201になります。一致したお試しが一時停止中・取消済みの場合は、切り替えも発行も行わず、自社Stripe連携の履歴に理由を記録します。このとき受け取りは404のままになるため、販売元は履歴を確認して購入者へ連絡してください。
- お試しライセンスの発行時に返却される
license_idを、販売元のバックエンドで購入者と関連付けて保存します。ライセンスキーではなくlic_...形式のIDを使用します。 - 購入開始時、販売元のバックエンドでStripe Checkout Sessionを作成し、保存したIDを
metadata[usakey_license_id]に、製品アプリが生成したchallengeをmetadata[usakey_claim_challenge]に設定します。製品アプリやブラウザから渡された任意のライセンスIDをそのまま信用せず、ログイン中の購入者に紐づく有効なお試しライセンスであるかを確認してください。 success_urlに?session_id={CHECKOUT_SESSION_ID}を含めることで、販売元の購入完了ページでSession IDを取得します。デスクトップアプリへ戻す場合は、そのページから製品固有のディープリンク経由で渡すか、利用者がアプリへ貼り付け可能な申込番号として提示します。- 製品アプリは保存済みの
claim_verifier、Idempotency-Key、および取得できた場合のSession IDを受け取りエンドポイントへ送信します。NOT_REQUIREDの場合は新しいキーの発行を待たず、保存済みのキーで定期確認を実行し、更新後の利用ルールと有効期限を取得します。
お試しライセンスを引き継ぐこの手順では、購入者ごとのIDを安全にmetadataへ設定するために販売元のバックエンドが必要です。バックエンドの認証情報を使用しない静的なPayment Linkは、新しいライセンスを発行する通常購入には利用できますが、この引き継ぎ手順の代替としては利用できません。
購入後の状態変化
支払いの失敗や解約は、Usakeyが自動的にライセンスの状態へ反映します。製品アプリ側から見るとライセンス状態の定期確認の結果が変化するだけであるため、購入連動のための判定ロジックを個別に実装する必要はありません。期限切れ・一時停止では端末の認証は残るため、入金や再開の後は同じ端末でそのまま使えます(下表)。
- 支払いの確認・契約の更新(Stripeの active / trialing)
- 有効(active)。期限はStripeの契約期間の終わりに合わせて更新します(短くなる場合もあります)
- 更新時の支払い失敗・支払い待ち・一時停止(past_due / unpaid / incomplete / paused)
- 一時停止中(suspended)
- 更新の入金確認が期限に間に合わない
- 入金を確認するまで期限切れ(expired)。端末の認証は残り、入金後に有効へ戻ると同じ端末でそのまま使えます
- 契約終了(canceled / incomplete_expired)
- 取消済み(revoked)。取り消した後は再開しません
- 購入の全額返金(一回払い、または購読の最初の請求)
- 取消済み(revoked)。一部返金では変わりません
- 購読の更新分だけの全額返金(契約は続いている)
- 一時停止中(suspended)。取り消しはしません。契約が続いていれば、Stripeの次の通知で契約の状態に合わせて再開し、解約されていれば取り消します
- 販売元がConsoleやManagement APIで一時停止したライセンス
- 支払いを確認しても再開しません(期限と利用ルールだけStripeに合わせます)。再開は販売元が行います
初回の購入では、支払いが確定するまでライセンスを発行しません。同一の通知が重複して届いた場合でも、ライセンスが重複発行されることはありません。Usakey側で処理済みの通知を記録しているため、その結果は管理画面の自社Stripe連携にある履歴から確認できます。
Stripeからの通知は発生順どおりに届かない場合があります。サブスクリプションに関する通知では、イベント名のみで過去の状態へ戻すことはせず、Stripeから最新のSubscriptionを取得して active / suspended / revoked を判定します。
購入情報より先に解約や全額返金の通知が届いた場合は「反映待ち」として保持し、後から購入情報が届いた時点で同一の申込を照合して対応するライセンスを取り消します。Stripeが通知を再送できる最長の期間(30日)に余裕を足した35日を過ぎても購入情報が届かない全額返金は、Usakeyの購入に対応しない決済のものとして待機を終え、履歴に理由を残します。
購入記録にまだ結び付いていない全額返金が待機中でも、Stripeの種別をテスト用からプロダクション用へ切り替えられます(以前の環境の待機は切り替え時に終了します)。
第2部 実装リファレンス
完全オフライン端末の手順
Usakeyへ一度も接続できない端末では、対象端末で作成する利用申請ファイル .usakeyreq と、管理者が発行する利用証明ファイル .usakeylic をやり取りします。
申請ファイルの作成と、利用証明ファイルの検証・読み込みは公式SDKが行います。 製品アプリには、ファイルを書き出す・読み込む操作と、その結果の表示を用意します。管理者は管理画面で申請ファイルを受け取り、利用証明ファイルを発行します。
手順1: 対象端末で .usakeyreq を作成する
製品アプリに「オフライン利用の申請ファイルを作る」操作を用意し、SDKの次の関数を呼び出して、戻り値をファイルへ保存します。出力先に同じ名前のファイルがある場合は上書きせず、別の名前を選ばせてください。
- Client::export_offline_activation_request
- Rust(
offlinefeature)。署名済みの申請ファイルの内容を作成します。 - export_offline_request
- 各言語ので使う同じ機能です。名前の書き方は言語ごとのREADMEを参照してください。
- 対象端末: SDKが、端末の署名鍵とP-256方式の受取用鍵、製品ID、指紋化した端末情報、一度だけ使う乱数、作成時刻を正規化したJSONにまとめて署名し、申請ファイルの内容を作ります。ライセンスキーと秘密鍵は含めません。作成から7日を超えた申請と、作成時刻が5分を超えて未来の申請は、発行するときに拒否されます。
- オンラインの管理者: 管理画面のライセンスで対象を開き、「インターネットへ接続できない端末で使う」から
.usakeyreqをアップロードし、発行された.usakeylicをダウンロードします。.usakeyreqにライセンスキーは含まれず、この画面で開いたライセンスに結び付けられます。 - 対象端末: USBメモリなどで
.usakeylicを持ち込み、製品アプリで読み込みます(手順2)。
手順2: 発行された .usakeylic を読み込む
製品アプリに「利用証明ファイルを読み込む」操作を用意し、SDKへファイルの中身を渡します。SDKは、ZIP内の trust-bundle.json、manifest.paseto、certificate.paseto(ユーザー単位のライセンスでは named-user-proof.paseto も)を取り出し、、ファイルの目録(manifest)の署名、全ファイルのサイズとSHA-256、証明書の署名、製品・ライセンス・端末鍵との結び付き、発行期限と契約期限を検証します。1項目でも一致しない場合は取り込みません。
- Client::import_offline_activation_package
- Rust。ファイルの中身を検証し、成功した場合だけ利用可能な状態にします。
- Client::import_and_store_offline_activation_package
- Rust。検証に成功したファイルを、暗号化した保存先(
OfflineLicenseStore)へ保存します。 - Client::load_stored_offline_activation_package
- Rust。再起動後に保存済みのファイルを読み込み、そのたびに最初から検証し直します。
- import_offline_package
- 各言語のbindingの読み込み関数です。新しく開いたクライアントで、オンライン操作の前に1回だけ呼べます。失敗した場合も含め、同じクライアントで2回目を呼ぶと
OfflineImportUnavailable(C ABIではUSAKEY_STATUS_OFFLINE_IMPORT_UNAVAILABLE)になります。ただし、期待するnamed userのIDの形式が誤っている場合は、packageを読む前に断るため1回目を消費しません。再起動後や失敗後は、新しいクライアントを開いて呼び直してください。
ユーザー単位のライセンスでは、想定する利用者と割当を必ず渡して読み込みます。 上の関数だけで読み込むと拒否されます。Rustでは Client::import_named_user_offline_activation_package(保存は import_and_store_named_user_offline_activation_package、再起動後は load_stored_named_user_offline_activation_package)に、OfflineNamedUserIdentity::new(割当ID, ライセンスユーザーID) を渡します。各言語のbindingでは import_offline_package の第2引数(C ABIでは usakey_client_import_offline_package の expected_named_user)に同じ2つを渡します。割当ID(las_…)とライセンスユーザーID(lsu_…)は、Management APIの GET /mgmt/v1/licenses/{id}/assignments で確認できます。
期限が近づいたら、同じ端末で申請ファイルを作り直し、発行し直します。同じ端末(同じ端末の鍵)へ発行し直しても、端末の枠や発行の枠は増えません。保存するのは、発行されたファイル全体(署名済みの公開鍵一覧・証明書・目録を含む)です。端末の秘密鍵やライセンスキーは保存しません。保存先はアプリのデータ用フォルダなど、利用者ごとに分かれ、他の利用者から読み書きできない場所にしてください。
暗号化したファイル(ファイルの暗号化)の鍵も、利用証明ファイルで届けられます。 申請ファイルの任意の項目 sealed_file_headers(暗号化したファイルのヘッダーのバイト列をbase64urlにした文字列の配列。1〜16件、重複なし。device_proof の署名の対象です)に入れると、発行するときにファイルごとにプラン・ヘッダー・製品・鍵・権利名を確かめ、利用証明ファイルに sealed-file-keys.json(目録の対象)を入れます。1件でも鍵を渡せないファイルがあれば発行せず、管理画面とManagement APIに理由を示します。申請の記録には、ヘッダーそのものではなくSHA-256だけを残します。読み込んだあとは、通信せずにそれらのファイルを開けます(暗号化したファイルの鍵を受け取る)。
再起動したとき
完全オフラインの端末では、保存済みの利用証明ファイルを起動のたびに読み込み、最初から検証し直してから利用を許可します。
- 契約期限を超えて利用を許可することはありません。
- 利用証明ファイルは、目録と証明書のうち早いほうの期限が絶対の上限です。通信猶予による延長はありません。
- 同時利用数を制御するライセンスは、完全オフラインでは使えません。
- 検証に失敗したファイルや期限を過ぎたファイルで、以前の利用許可へ戻すことはありません。
.usakeyreq JSONの例
{
"format": "usakey-offline-request-v1",
"product_id": "prod_...",
"device_signing_public_key": "ed25519:...",
"device_kem_public_key": "p256:...",
"machine_signals": { "installation": "<SHA-256 hex>" },
"machine_info": { "os": "linux", "arch": "x86_64" },
"sdk_version": "usakey-rust/0.1.0",
"app_version": "1.0.0",
"nonce": "<16-byte random Base64URL>",
"request_id": "<UUID>",
"generated_at": 1787623200,
"display": { "product_name": "<製品名>", "device_code": "A1B2C3D4E5F6" },
"device_proof": "<Ed25519 signature Base64URL>"
}machine_signals と display は、Rust SDKの OfflineActivationOptions で指定したときだけ入ります。各言語のbindingが作る申請ファイルには入りません。SDKが返す12桁の端末コードと、発行後のmanifestの8桁の照合コード(XXXX-XXXX)は、どちらも同じ端末の署名用公開鍵のSHA-256の先頭部分です(8桁のコードは12桁のコードの先頭8桁と同じです)。
申請ファイルは64 KiB以内のJSONオブジェクトとしてください。対応形式は usakey-offline-request-v1 です。端末情報は認証の判断に必要な最小限に絞り、巨大な配列や深い入れ子構造は受け付けません。同じ端末に発行する利用証明ファイルは、24時間に10件までです。
超えると、新しい申請ファイルでも発行せず(Management APIは 422 OFFLINE_PACKAGE_INVALID の details.reason=package_limit_reached)、時間をおいてからやり直します。同じ申請ファイルを送り直したときは、作成済みの利用証明ファイルをそのまま返します。
device_proof(端末が秘密鍵を保持していることを示す署名)の入力は初回の端末認証と同様で、device_proof 自身を除いたJSONの Usakey-Activation-Proof-v1\n + JCS(payload) です。
生のシリアル番号やライセンスキー全文は含めません。発行後のmanifest(ファイル内容と用途を示す署名付き目録)には、取り違え防止用の製品名、ライセンスIDとライセンスキーの一部、8桁の端末照合コード、発行時刻と有効期限が含まれます。完全オフライン環境では同時利用制御、期限前の取り消し反映、リアルタイムの異常検知は行えず、証明書の expires_at までの端末固定利用に限定されます。
第2部 実装リファレンス
応答の検証順序
Usakeyの応答が正当な送信元から届き、途中で改ざんされていないことを、次の順序で確認します。
製品へ事前に同梱
最初に信頼する公開鍵
この鍵のみネットワーク経由で取得せず、製品の配布物へ安全に組み込みます。設定値は管理画面の「接続設定をダウンロード」に含まれます。
公開鍵一覧
Usakeyの署名鍵を確認
一覧の署名・有効期限・更新番号を確認し、古い一覧への差し替えを防ぎます。
短期鍵の委任証明
応答用の公開鍵を確認
製品長期鍵の署名、用途、製品ID、および6〜24時間の有効期間を確認します。
API応答
送信内容と署名を照合
製品ID、一度だけ使用する乱数、HTTP状態、本文の指紋、短期鍵による署名を確認します。
製品アプリ
検証後にのみ判定を利用
利用証明の製品・端末・有効期限も確認し、初めて対象機能へ反映します。
- 製品にあらかじめ組み込んだ「最初に信頼する公開鍵」を使用し、署名検証用の公開鍵一覧(API項目名:
Trust Bundle)の署名と有効期限を確認します。 parent_key_idに対応する製品長期公開鍵でsigning_delegationの署名を検証し、製品ID、用途、online_key_id、および6〜24時間の有効期間を確認します。- 応答の照合情報(
response_assertion)が、製品・親鍵・短期鍵・一度だけ使用する乱数・HTTP状態と一致するか確認します。 - 判定結果を正規化JSONへ整形してSHA-256の指紋を生成し、
body_sha256と一致するか確認します。 - 委任証明内の短期Ed25519公開鍵を用いて
response_signatureを検証します。これにより、「正当なUsakeyが生成した応答であること」および「途中で改ざんされていないこと」を確認します。
正規化JSONの作り方
- オブジェクトのキーを文字列として昇順に並べ、キーの重複を禁止する
- 配列の順序は変更せず、要素間やコロンの前後に空白を入れない
- 数値は整数のみを許可し、浮動小数点数は禁止する
- 文字列は有効なUTF-8としてJSONエスケープし、真偽値とnullは
true/false/nullとする
例: {"b":2,"a":["x",true]} は {"a":["x",true],"b":2} になります。
開発用のUsakeyサーバーが自分で署名している場合(signing_mode が development_self_signed)、その公開鍵は開発用です。開発用の公開鍵を、製品として配布するアプリの「最初に信頼する公開鍵」に使わないでください。
付録
用語集
このページでは日本語名を使用し、API上の名称を括弧書きで併記しています。プログラムの判定で使用するのは、括弧内の名称です。
- Runtime API(製品アプリ組み込み)
- 顧客へ配布する製品アプリが、端末認証やライセンス状態の確認に使用するAPIです。このページで説明しています。自社システムからUsakeyを操作するAPIはManagement APIと呼び、外部システム連携のページで説明しています。
- 端末のライセンス認証
- API項目名は
activation。ライセンスと端末の鍵を関連付け、その端末での利用を許可することです。 - ライセンス状態の定期確認
- API項目名は
heartbeat。利用中の端末が、現在も利用可能であるかを定期的に確認する通信です。 - 同時利用枠
- API項目名は
lease。同時利用型ライセンスにおいて、現在起動しているプロセスや端末が一時的に確保する利用枠です。 - 署名検証用の公開鍵一覧
- API上の名称は
Trust Bundle。製品アプリがUsakeyの応答署名を検証するための公開情報です。 - 製品長期鍵
- Trust Bundleに含まれる製品単位の公開鍵です。ライセンス証明書や、短期オンライン鍵の委任証明を検証します。有料プランでは製品専用の取り出せない鍵、検証プランでは共有の発行用鍵が承認した製品別の暗号化鍵を使用します。
- 短期オンライン鍵
- Runtime応答の署名のみを行う、有効期間が6〜24時間の鍵です。製品長期鍵による委任を検証してから利用します。
- 公開鍵一覧の更新番号
- API項目名は
sequence。前回より小さい番号の一覧を拒否し、古い公開鍵一覧への差し替えを検知します。 - 一度だけ使う乱数
- API項目名は
nonce。コピーした要求を再利用する攻撃を防ぐため、要求ごとに生成する乱数です。 - 本文の指紋
- SHA-256。内容から生成する64桁の値で、1文字でも変われば別の値になります。暗号化ではありません。
- 正規化JSON
- JCS。空白やキー順の違いをなくし、どの環境で計算しても同じJSON表現になるよう整える規則です。
- Base64URL
- バイナリデータを、URLやHTTPで安全に扱える文字列へ変換する形式です。暗号化ではありません。
- C ABI
- プログラミング言語をまたいで関数を呼び出すための共通の約束事です。各言語のSDKはこれを通じて、Rustで書かれた同じ本体を呼び出します。
- バインディング
- 各言語から、Rustで書かれたSDK本体を呼び出すための薄い橋渡しのコードです。署名の検証やライセンスの判定は本体が行います。
- 安全側に倒す
- 英語では
fail closed。状態を確かめられないときに、保護対象の機能を利用できない側で判定することです。 - 利用申請ファイル
- 拡張子は
.usakeyreq。完全オフラインの端末が作る、署名付きの申請ファイルです。ライセンスキーと秘密鍵は含みません。 - 利用証明ファイル
- 拡張子は
.usakeylic。管理画面で申請ファイルから発行する、その端末でだけ使える署名付きのファイルです。
付録
エラー形式
エラーはHTTP状態コードとともに、次のJSON形式で返されます。この非2xx JSONは、2xx成功時の署名付き応答ではありません。HTTPSを正しく検証し、JSONの構造を確認した上で取り扱ってください。APIが返す code はプログラムの分岐に、messageはログや調査に、request_idはお問い合わせ時の照合に使用します。doc_url はこの説明ページのURLです。
{
"error": {
"code": "LICENSE_EXPIRED",
"message": "The license term has ended.",
"request_id": "…",
"retryable": false,
"doc_url": "https://usakey.jp/docs#errors",
"details": {}
}
}公式SDKがアプリへ公開している安定したエラーコード(将来も意味が変わらないと約束したもの)を取得できるのは、Rust本体の Error::stable_api_code()(BlockingSession では、失敗の直後に同じスレッドで usakey::session::take_runtime_error_code())、C ABIの usakey_last_runtime_error_code()、Pythonの RuntimeFailureError.code、Rubyの RuntimeFailureError#code、Node.js / TypeScriptの RuntimeFailureError.apiCode、PHPの RuntimeFailureException の apiCode に限られます。その他のでは、公開されている状態(status)、失敗の種類(failure kind)、再試行の目安(retry hint)だけを使い、messageの文字列を解析したり、取得できないコードで分岐したりしないでください。言語ごとに取得できる情報の一覧はSDK ZIP内の docs/support-matrix.md に、安定コードの分類表はスキルZIP内の references/runtime-protocol.md に記載されています。
retryable が true の場合は、時間を空けることで同じ操作が成功する可能性があることを示していますが、再試行の方法は操作ごとに異なります。現在該当するのは、上限超過(RATE_LIMITED)、同一の Idempotency-Key の処理中(IDEMPOTENCY_IN_PROGRESS)、上限管理ストアの一時的な障害(RATE_LIMIT_STORE_UNAVAILABLE)、製品署名鍵の準備中(PRODUCT_SIGNING_KEY_PENDING)の4点です。
Management APIのライセンス発行で署名鍵準備中になった場合は、数分待ち、同じ本文と同じ Idempotency-Key で再試行します。オフライン利用ファイルの発行では冪等キーを使わず、同じ要求ファイルと同じ指定内容で再試行します。
Runtime APIの署名鍵準備中はHTTP 503と Retry-After を使います。定期確認や同時利用枠の操作などの認証済み要求を再試行する際は、現在時刻、新しいnonce、新しい署名を生成し直し、同じバイト列を再送しないでください。
例外となるのは、初回の端末認証を送信した後に応答を受信できず、結果が不明となった場合です。この場合の再送は公式SDKに任せてください。SDKは送信した本文のバイト列そのものと同一の Idempotency-Key で1回だけ自動再送します。本文を作り直すとハッシュ値が変わり、同じ冪等キーでは冪等性の競合になるためです。SDKが結果不明を返した後に、利用者または運用者が明示的な回復操作として端末認証をやり直す場合は、新しい本文と新しい Idempotency-Key を使用してください。
LICENSE_EXPIRED や ACTIVATION_DEACTIVATED のような確定的な拒否については再試行を行わず、製品アプリ側の判定表 に従って状態を表示してください。
OpenAPIに掲載されているRuntime / Management API各エンドポイントのHTTP応答やエラーJSONの形式は、APIリファレンスで確認できます。安定コードの分類は上記のスキルZIP内資料を参照してください。