s1r-j.hatenablog.com Open in urlscan Pro
13.115.18.61  Public Scan

Submitted URL: http://s1r-j.hatenablog.com/
Effective URL: https://s1r-j.hatenablog.com/
Submission: On May 17 via api from JP — Scanned from JP

Form analysis 1 forms found in the DOM

GET https://s1r-j.hatenablog.com/search

<form class="search-form" role="search" action="https://s1r-j.hatenablog.com/search" method="get">
  <input type="text" name="q" class="search-module-input" value="" placeholder="記事を検索" required="">
  <input type="submit" value="検索" class="search-module-button">
</form>

Text Content

読者になる



S1R-Jの技術ブログ


とあるSEの技術ブログ

2022-04-30


SINONのUSEFAKETIMERSはSETTIMEOUTと一緒に使用できない

Qiitaに書いたのでリンクだけ貼っておく。

sinonのuseFakeTimersはsetTimeoutと一緒に使用できない - Qiita

s1r-J 16日前




広告を非表示にする

 * もっと読む

コメントを書く
2022-04-22


WEB AUTHENTICATION API 9章 WEBAUTHN拡張機能の日本語訳

FIDO 日本語訳 認証・認可

W3CのWebAuthnの仕様書の9章 拡張機能について書かれている部分について、読んだので残しておく。

前にWeb Authentication API 7章 Relying Party処理の日本語訳 - s1r-Jの技術ブログについては日本語訳した。


9 WEBAUTHN拡張機能

公開鍵クレデンシャルを生成する機構は、認証アサーションの要求・生成と同じく5章 Web Authentication
APIで定義されており、特定のユースケースに沿うように拡張することができる。それぞれのケースは 登録拡張機能 ( registration extension
)およびまたは 認証拡張機能 ( authentication extension )を定義することで対応されている。

すべての拡張機能は クライアント拡張機能 ( client extension
)であり、つまり拡張機能はクライアントと通信し、クライアントによって処理される。クライアント拡張機能は以下のステップとデータを定義している:

 * navigator.credentials.create()拡張機能 登録拡張機能のためのリクエストパラメータとレスポンス値
 * navigator.credentials.get()拡張機能 認証拡張機能のためのリクエストパラメータとレスポンス値
 * 登録拡張機能および認証拡張機能に対するクライアント拡張機能の処理

公開鍵クレデンシャルの生成または認証アサーションの要求の際、WebAuthnリライングパーティは一連の拡張機能の利用を要求することができる。これらの拡張機能はクライアントおよびまたはWebAuthn認証器によってサポートされている場合、要求処理中に呼び出される。リライングパーティは、get()(認証拡張機能のため)の呼び出し中またはcreate()(登録拡張機能のため)の呼び出し中に、各拡張機能のためのクライアント拡張機能インプットをクライアントに送る。クライアントは、クライアントプラットフォームがサポートする各拡張機能に対してクライアント拡張機能処理を実行し、拡張機能識別子およびクライアント拡張機能アウトプット値を含めた各拡張機能で定義されているクライアントデータを拡張します。

また、拡張機能は 認証拡張機能 ( authenticator extension
)でもあり、つまり拡張機能は認証器と通信し、認証器によって処理される。認証拡張機能は以下のステップとデータを定義している:

 * authenticatorMakeCredential拡張機能 登録拡張機能のためのリクエストパラメータとレスポンス値
 * authenticatorGetAssertion拡張機能 認証拡張機能のためのリクエストパラメータとレスポンス値
 * 登録拡張機能および認証拡張機能に対する認証器拡張機能の処理

認証器拡張機能に対して、認証器拡張機能処理の一部としてクライアントも各拡張機能のためのCBOR
認証器拡張機能インプット値(しばしば対応しているクライアント拡張機能インプット値に基づいて)を作成し、get()(認証拡張機能のため)の呼び出し中またはcreate()(登録拡張機能のため)の呼び出し中に認証機にそれらを受け渡す。これら認証器拡張機能インプット値はCBORで表され、ネーム・バリューのペアで受け渡される。拡張機能識別子はネームとして、対応する認証器拡張機能インプットはバリューとして扱われる。次に、認証器はサポートしている拡張機能にたいして追加の処理をおこない、格納機能によって定義されるCBOR
認証器拡張機能アウトプットを返す。
認証器拡張機能に対するクライアント拡張機能処理の一部は、クライアント拡張機能アウトプットを作成する入力値として認証器拡張機能アウトプットを利用する。

すべてのWebAuthn拡張機能はクライアントおよび認証器にとってOPTIONALである。つまり、リライングパーティから要求されたいかなる拡張機能でもクライアントブラウザまたはOSによって無視されたり、認証器に渡されなかったりすることがあり(MAY)、また認証器によって無視されることがある(MAY)。拡張機能を無視することはWebAuthn
APIの処理では失敗として扱われることは決してなく、リライングパーティがどのAPI呼び出しでも拡張機能を含めたときでも、一部もしくは全ての拡張機能が無視されるケースについてハンドリングする準備しなければならない(MUST)。

可能なかぎり多くの拡張機能にサポートしたいクライアントは、認識できない拡張機能を認証器に渡すことを選んでもよく(MAY)、シンプルにクライアント拡張機能インプットをCBORにエンコードした認証器拡張機能インプットを生成する。すべてのWebAuthn拡張機能はこの実装選択がユーザのセキュリティやプライバシーを危険に晒さない方法で定義されていなければならない(MUST)。例えば、クライアント処理を要求する拡張機能の場合、意味のない認証器拡張機能インプット値を生成するようなナイーブな受け渡しを保証するような方法で定義されると、結果としてその拡張機能は認証器によって無視される。すべての拡張機能はOPTIONALであることから、API処理中の機能的失敗を発生させることはない。同様にクライアントは、CBOR出力がJSONだけに存在する型を使用する場合、認証器拡張機能アウトプット値をJSONにエンコードしたことで解釈できない拡張機能のクライアント拡張機能アウトプット値の処理を選ぶことができる。

クライアントは認識できない拡張機能を受け渡すと選択したとき、クライアント拡張機能インプットのJavaScriptの値を認証器拡張機能インプットのCBORの値に変換する。JavaScriptの値が%ArrayBuffer%の場合、CBOR
byte
arrayに変換される。JavaScriptの値が非整数値の場合、64ビットCBOR浮動小数点数に変換される。一方、JSONの型に対応したJavaScriptの型の場合、変換は[RFC8949]の6.2節(JSONからCBORへの変換)で定義されているルールに従って実施されますが、JSONの型の値のインプットに対してではなくJavaScriptの型の値のインプットに処理をおこないます。これらの変換が実施された場合、変換結果のCBORの正規化はCTAP2
カノニカルCBORエンコード形式を使って実行しなければならない(MUST)。

JavaScriptの数値変換ルールは、クライアントが認識していない拡張機能を受け渡すときに拡張機能が浮動小数点数を利用していた場合、認証器はCBOR整数としてそれらの値を受け取る準備が必要であり、認証器は実際のクライアントの助けなしに常に動作すべきということに注意する。これは使われている浮動小数点数がたまたま整数である場合に発生する。

同様に、クライアントが認識していない拡張機能からアウトプットを受け取ったとき、認証器拡張機能アウトプットのCBORの値はクライアント拡張機能アウトプットのJavaScriptの値に変換される。CBORの値はバイト文字列のとき、JavaScript
%ArrayBuffer%(base64urlエンコードされた文字列ではなく)に変換される。一方、CBORの型はJSONの型に対応しているとき、変換は[RFC8949]の6.1節(CBORからJSONへの変換)に定義されているルールに従って実施されるが、JSONの型の値ではなくJavaScriptの型の値のアウトプットが生成される。

一部のクライアントは機能フラグのもとでこの受け渡し機能を実装することを選択する可能性があることに注意する。認証器が新しい拡張機能を実験することを許可し、クライアントで明示的にサポートする前にリライングパーティがそれらを利用できるようになり、この機能をサポートすることでイノベーションを促進する。

[RFC8809]によって設立されたIANA「WebAuthn Extension Identifiers」レジストリ
[IANA-WebAuthn-Registries] は登録済WebAuthn拡張機能の最新のリストについて協議する。


9.1 拡張機能識別子

拡張機能は、拡張機能の作成者が決定した 拡張機能識別子 ( extension identifier )と呼ばれる文字列によって識別されています。

拡張機能識別子は、[RFC8809]によって設立されたIANA 「WebAuthn Extension
Identifiers」レジストリ[IANA-WebAuthn-Registries]に登録すべきである(SHOULD)。当然ではあるが登録されている拡張機能識別子はユニークである。

すべての拡張機能識別子は最大32オクテット長でなければならず(MUST)、バックスラッシュおよびダブルクォート、つまり%x22および%x5cを除いた[RFC5234]で定義されているVCHARを除く印字可能USASCII文字だけで構成されなければならない(MUST)。実装はケースセンシティブにWebAuthn拡張機能識別子に一致しなければならない(MUST)。

複数のバージョンが存在する可能性がある拡張機能は、それらの識別子にバージョンが含まれていることに注意するべきである。実際、つまり異なるバージョンは異なる拡張機能として扱われる。例:myCompany_extension_01

10章
定義済拡張機能は追加の拡張機能および拡張機能識別子の一覧を定義する。登録済のWebAuthn拡張機能識別子の最新リストのための[RFC8809]によって設立されたIANA
「WebAuthn拡張機能識別子」レジストリ [IANA-WebAuthn-Registries]を参照する。


9.2 拡張機能の定義

拡張機能の定義では、拡張機能識別子、get()またはcreate()を介して送られたクライアント拡張機能インプット引数、クライアント拡張機能処理のルール、そしてクライアント拡張機能アウトプットを定義する。認証器と連携する拡張機能(つまり認証器拡張機能)の場合、authenticatorGetAssertionまたはauthenticatorMakeCredentialの呼び出しによってCBOR
認証器拡張機能インプット引数、認証器拡張機能処理のルールおよびCBOR 認証器拡張機能アウトプットの値も定義しなければならない(MUST)。

クライアントによって処理されたすべてのクライアント拡張機能はクライアント拡張機能アウトプットを返却しなればならず(MUST)、WebAuthnリライングパーティはクライアントによって利用された拡張機能を知ることになる。同じように、認証器での処理を必須とするすべての拡張機能は、リライングパーティが認証器によって利用された拡張機能を知るために認証器拡張機能アウトプットを返却しなければならない(MUST)。拡張機能が如何なる結果の値も必須としない場合、JSON
Boolean
クライアント拡張機能アウトプットの結果をその拡張機能が理解されて処理されたことを知らせるためにtrueをセットして返却するように定義すべきである(SHOULD)。同様に如何なる結果の値も必須としない認証器拡張機能は値を返却しなくてはならず(MUST)、CBOR
Boolean 認証器拡張機能アウトプットの結果をその拡張機能が理解されて処理されたことを知らせるためにtrueをセットして返すべきである(SHOULD)。


9.3 リクエストパラメータの拡張

拡張機能は1つか2つのリクエスト引数を定義する。値がJSONにエンコードされている クライアント拡張機能インプット ( client extension
input )は、get()またはcreate()の呼び出しにおいてWebAuthnリライングパーティからクライアントに渡され、一方で 認証器拡張機能 (
authenticator extension input
)向けのCBOR形式の認証器拡張機能インプットはそれらの呼び出し時にクライアントから認証器に渡される。

リライングパーティは同時に拡張機能の利用を要求し、create()またはget()の呼び出しに対してextensionsのオプションにエントリを含めることでそのクライアント拡張機能インプットをセットする。

注意:他のドキュメントでは、拡張機能インプットがエントリキーとして拡張機能識別子を常に利用しているとは限らないと定義していた。新しい拡張機能には上述の変換に従うべきである(SHOULD)。

EXAMPLE 10

var assertionPromise = navigator.credentials.get({
    publicKey: {
        // Other members omitted for brevity
        extensions: {
            // An "entry key" identifying the "webauthnExample_foobar" extension, 
            // whose value is a map with two input parameters:
            "webauthnExample_foobar": {
              foo: 42,
              bar: "barfoo"
            }
        }
    }
});


拡張機能の定義は、クライアント拡張機能インプットとして正当な値を定義しなくてはならない(MUST)。クライアントは不正なクライアント拡張機能インプットを持つ拡張機能を無視すべきである(SHOULD)。拡張機能がリライングパーティからのパラメータを一切必要としない場合、リライングパーティから要求された拡張機能が処理されたことを示すためにtrueを設定するBoolean型のクライアント引数を取るように定義すべきである(SHOULD)。

クライアントでの処理に対してだけ影響する拡張機能は、認証器拡張機能インプットを定義する必要がない。認証器で処理をおこなう拡張機能は、クライアント拡張機能インプットから認証器拡張機能インプットの算出方法を定義しなければならず(MUST)、AuthenticationExtensionsAuthenticatorInputsおよびAuthenticationExtensionsAuthenticatorOutputsのCDDLを、エントリキーとして拡張機能識別子を利用して$$extensionInputおよび$$extensionOutput
グループソケットに対する追加選択を定義することで定義しなければならない(MUST)。インプットパラメータを必須としない、つまりBoolean型のクライアント拡張機能インプットの値をtrueに設定する拡張機能は、認証器拡張機能インプットもBoolean値true(CBOR
major type 7, value 21)として定義するべきである(SHOULD)。

以下の例では、識別子
WebauthnExample_foobarを持つ拡張機能が認証器拡張機能インプットとして符号なし整数を取り、認証器拡張機能アウトプットとして最小1バイト列の配列を返すことを定義している:

EXAMPLE 11

$$extensionInput //= (
  webauthnExample_foobar: uint
)
$$extensionOutput //= (
  webauthnExample_foobar: [+ bytes]
)


注意:拡張機能は認証器引数をできる限り小さくなることを目指して定義すべきである。一部の認証器はBluetooth
Low-EnergyやNFCのような低帯域接続による通信をおこなっている。


9.4 クライアント拡張機能処理

拡張機能は、クレデンシャルの作成またはアサーションの生成のあいだにクライアントでの追加処理の要求を定義しているかもしれない(MAY)。拡張機能に対するクライアント拡張機能インプットはクライアント処理に対する入力として利用される。サポートされている各クライアント拡張機能に対し、クライアントは
clientExtensions mapに対して拡張機能識別子をキー、クライアント拡張機能インプットをバリューとして拡張機能のエントリを追加する。

同様に、クライアント拡張機能アウトプットは、拡張機能識別子をキー、各拡張機能の クライアント拡張機能アウトプット ( client extension
output
)の値をバリューとしてgetClientExtensionResults()の結果のディクショナリとして表現される。クライアント拡張機能インプットと同じく、クライアント拡張機能アウトプットはJSONにエンコードされた値である。無視した拡張機能に対しては如何なる値も返却してはならない(MUST
NOT)。

認証器処理を必須とする拡張機能は、CBOR
認証器拡張機能インプットを決定するためのクライアント拡張機能インプットを利用する処理およびクライアント拡張機能アウトプットを決定するためのCBOR
認証器拡張機能インプットを利用する処理を定義しなければならない(MUST)。


9.5 認証器拡張機能処理

処理された各認証器拡張機能のCBOR
認証器拡張機能インプットの値は、authenticatorMakeCredentialおよびauthenticatorGetAssertionの処理の拡張機能パラメータに含まれる。拡張機能パラメータは各キーが拡張機能識別子で対応する値が認証器拡張機能インプットとなっているCBORマップである。

同様に、拡張機能アウトプットはauthenticator dataのextensions部分に表記されている。authenticator
dataのextensions部分は、各キーが拡張機能識別子で対応する値が 認証器拡張機能アウトプット ( authenticator extension
output )となっているCBORマップである。

サポートされている各拡張機能に対し、認証器拡張機能処理ルールは認証器拡張機能インプットから認証器拡張機能アウトプットを生成するために利用され、その他入力値に対しても可能な限り同様である。無視する拡張機能に対しては如何なる値も返却してはならない(MUST
NOT)。

WebAuthn FIDO

s1r-J 24日前




広告を非表示にする

 * もっと読む

コメントを書く
2022-03-21


MKCERTを使ってLOCALHOSTをHTTPS化して開発する

環境構築

以前も同じ内容の記事を書きましたが、auth0のブログにあった方法のほうが便利そうなので、試してみました。
auth0のブログの内容をちょこちょこ翻訳しながら紹介します。

元になったブログ記事はこちら

Why and How to Use HTTPS in Your Local Development Environment

auth0はOAuth・OIDCなどの認証認可基盤とよばれるようなシステムの開発会社です。認証認可(サインインやログインなど)に関するブラウザ等の機能(例:FIDO2認証)にはHTTPSが必要とされることがあり、ローカル開発環境にもHTTPSが必要になのでしょう。


試した環境

 * Windows 10
 * Chocolatey v0.12.1
   * mkcertをインストールするために必要
 * mkcert v1.4.3
   * HTTPSに必要な証明書などを作成するツール
 * Node.js v12.22.5


手順


MKCERTのインストール

HTTPSに必要な証明書などを作成するツールmkcertをインストールします。
OpenSSLが最もよく知られていて最も強力ですが、公開鍵基盤について充分な知識が必要なため、今回はmkcertを使うとのことです。

mkcertのインストール方法はmkcertのGitHubに記載されているとおりですが、WindowsなのでChocolateyを使います。WSLを使ってLinux版のインストール手順で実行することもできそうですね。

Chocolateyのインストール方法も公式サイトに記載されているとおりで出来ました。

Chocolatey Software | Installing Chocolatey

PowerShellを管理者権限で起動して以下のコマンドを実行

$ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

完了するまで待ち、完了したら以下のコマンドで確認します。

$ choco

バージョン情報が帰ってくれば完了です。 改めてmkcertのインストールをします。

$ choco install mkcert

インストールが完了すると、インストール成功という情報とバージョンの情報が返ってきました。


MKCERTでローカル認証局を作成する

以下のコマンドを実行します。

$ mkcert -install

ポップアップ画面で以下の画像のようなセキュリティ警告が表示されました。 どうやら、Firefoxの機能・設定に関する警告(Setting Up
Certificate Authorities (CAs) in Firefox | Firefox for Enterprise
Help)であり、Firefoxを使わないのであれば無視してよいそうです。



今回は「はい」を選ぶことにしました。


証明書の作成

まず、Node.js上に構築するアプリのフォルダを作成します。フォルダ名はブログ記事の内容と同じにしてmy-secure-appとします。
作成したフォルダ内に入ります。

フォルダ内で以下のコマンドを実行し、localhostというホスト名に対する証明書(localhost.pem)と秘密鍵(localhost-key.pem)を作成します。

$ mkcert localhost


サーバのソースコード

先のmy-secure-appフォルダ内にserver.jsというファイルを作成し、以下のコードを書いて保存します。

const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('localhost-key.pem'),
  cert: fs.readFileSync('localhost.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("I'm HTTPS-enabled!");
}).listen(8080);

console.log("The server is listening to port 8080 with HTTPS enabled.");



実行

my-secure-appフォルダ内でPowerShellかコマンドプロンプトを立ち上げ、以下のコマンドを入力します。

$ node server.js

これによってhttps://localhost:8080でサーバがアクセスを待っています。 実際に上記のURLをChromeに入力すると、「I'm
HTTPS-enabled!」というメッセージがブラウザに表示されます。

アドレスを変更してhttp://localhost:8080にしてHTTPで接続を試みると、「ページは動作していません」というブラウザのエラー画面になります。

また、Firefoxでhttps://localhost:8080にアクセスを試みると、以下の画像のように警告が表示されます。証明書が無効になっているHTTPSのサイトにアクセスしたときに表示される画面と同じです。
mkcertでローカル認証局を作成したときの警告と関連しており、Firefoxがこの認証局を信頼していないということが原因です。



対策としては、Setting Up Certificate Authorities (CAs) in Firefox | Firefox for
Enterprise Helpに記載されている指定のいちにルート証明書を配置することらしい(ちゃんと読んでいない)。


証明書の情報

すこし証明書のなかみのを確認



発行先は自分のPCの自分のアカウントで、発行元のは自分のPCの自分のアカウントのmkcertとのこと。 有効期限は2年3ヶ月(27ヶ月)ですね。Let's
Encryptoよりも随分長い。こちらのほうが便利かもしれないですね。

詳細もスクショしようと思いましたが消すのめんどくさくなってきたので、文字情報だけで。

 * バージョン:3
 * 署名アルゴリズム:sha256RSA
 * DNS Name=localhost
 * 証明書のパスは、この証明書の直上がmkcertのローカルCAのルート証明書。中間証明書はなし。


おわりに

今回の方法はmkcertを使うことでlocalhost(127.0.0.1)を27ヶ月間更新する手間なくHTTPS化して開発することができます。また、WSLがなくてもWindows、Linux、Macで実行できます。

以前、書いた記事ではFreenomでドメインを取得して、certbotを使ってLet's
Encryptoの証明書を作成し、3ヶ月ごとに更新する必要がありました。

今回の記事のほうが更新頻度も少なくて楽。また、環境を他の開発者に共有したり、配布するときにこちらのほうが良さそう(Dockerで配布とかもできる??)なので、こちらの方法に切り替えていこうかなと思っています。


参考文献

 * https://auth0.com/blog/using-https-in-your-development-environment/
 * https://support.mozilla.org/en-US/kb/setting-certificate-authorities-firefox
 * https://github.com/FiloSottile/mkcert
 * https://chocolatey.org/
 * https://s1r-j.hatenablog.com/entry/2021/09/29/001812
 * https://blog.jxck.io/entries/2020-06-29/https-for-localhost.html

環境構築 証明書

s1r-J 57日前




広告を非表示にする

 * もっと読む

コメントを書く
2022-02-24


AWS EC2でCURL: (60) SSL CERTIFICATE PROBLEM: CERTIFICATE HAS EXPIRED

AWS


現象

EC2でcurlを実行したところ、以下のようなエラーが発生した(アドレスはダミー)。

$ curl https://example.com

curl: (60) SSL certificate problem: certificate has expired

証明書のエラーだったので、サーバ側(上の例だとhttps://example.com)に原因があると思っていたが、今回は違った。
エラーについて詳しく見るため、-vのコマンドを付けて確認した。アドレスなどダミーにして隠しておく。

$ curl https://example.com -v
* Rebuilt URL to: https://example.com
*   Trying 192.168.0.1...
* TCP_NODELAY set
* Connected to example.com (192.168.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOE:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, certificate expired (557):
* SSL certificate problem: certificate has expired
* Closing connection 0
curl: (60) SSL certificate problem: certificate has expired
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.


解決

原因はクライアント側(EC2インスタンス)のルート証明書の期限が切れていたことだった。
2021年1月あたりに作成したEC2インスタンスだったので、期限切れのままつかっていたのかもしれない。

解決方法については、AWS公式サイトに記載されていた。

EC2 インスタンスにある期限切れの Let’s Encrypt 証明書を修正する

今回使っていたEC2インスタンスはAmazon Linux2だったので、以下のコマンドを実行。

sudo yum install https://cdn.amazonlinux.com/patch/ca-certificates-update-2021-09-30/ca-certificates-2021.2.50-72.amzn2.0.1.noarch.rpm

証明書のインストールが完了したら、再度curlを実行したところ問題なく通信ができた。

—

Qiitaで同じことをしている人がいたので、こちらも記載しておく。

【AWS/EC2/Amazon Linux2】curl: (60) SSL certificate problem: certificate has
expired - Qiita

AWS EC2 curl SSL 証明書

s1r-J 82日前




広告を非表示にする

 * もっと読む

コメントを書く
2022-01-30


NODE.JSで証明書チェーンの検証をおこなう(JSRSASIGN編)

JavaScript

Node.jsでnode-forgeを使って証明書チェーンの検証をおこなう実装を前に書きました(Node.jsで証明書チェーンの検証をおこなう -
Qiita)が、jsrsasignを使うことが多くなったのでその方法についてもメモしておく。

実装は例によってjsrsasignのGitHubリポジトリのissueに挙げられていたので、ちょっと改変して載せておきます。

Verify Certificate against a root Cert · Issue #176 · kjur/jsrsasign


実装

先に書いたように今回はjsrsasignを利用します(前回はnode-forgeモジュール)。

実装は以下のとおりです。

証明書A -> 証明書B -> ルート証明書という順に上位の証明書になっていきます。
証明書の中身は実際に検証した証明書に変えてください。サンプルでは開発のときに試したFIDO Allianceの証明書を使っています。

This file contains bidirectional Unicode text that may be interpreted or
compiled differently than what appears below. To review, open the file in an
editor that reveals hidden Unicode characters. Learn more about bidirectional
Unicode characters
Show hidden characters

import jsrsasign from 'jsrsasign'; // 証明書A(サーバ証明書) "leaf certificate"とも const
certAStr = `-----BEGIN CERTIFICATE-----
MIIHZDCCBkygAwIBAgIMR79ApI1LvDScvJ+hMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTgwNgYDVQQDEy9HbG9iYWxTaWduIEV4dGVuZGVkIFZhbGlkYXRpb24gQ0EgLSBTSEEyNTYgLSBHMzAeFw0yMTA0MTIxOTU3MjRaFw0yMjA1MTQxOTU3MjRaMIIBIDEdMBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xETAPBgNVBAUTCEMzNDU0Mjg0MRMwEQYLKwYBBAGCNzwCAQMTAlVTMRswGQYLKwYBBAGCNzwCAQITCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSgwJgYDVQQJEx8yNTcwIFcuIEVsIENhbWlubyBSZWFsLCBTdGUgMTUwMRkwFwYDVQQLExBNZXRhZGF0YSBTZXJ2aWNlMRwwGgYDVQQKExNGSURPIEFMTElBTkNFLCBJTkMuMR0wGwYDVQQDExRtZHMuZmlkb2FsbGlhbmNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMj7AWgfM95ePexLcCuu+otyYFwLuKI811j7CPJzHOYQ/BgWvPIjvyPsOlmgetuUbxlhIr1nbd0BXy1oYF/Zuvoqy0/IYvb3R17FQa4bbhhhuVIXw40nuSGSUu5/z6Hmtu+kuGB3vq5wQGhxrn73Q4jn/dlwWnLT5suF6omSttasy99OZzFXQ/nIi6JSCANxfjRzek3y2uN5evjPnR12Eu/eXArNtw27jSPjSP+Gt5UHCiHnM9RL81uS13It73WmFj7g7vEBdfwiq/fwA/SqIu19JVK9Rvi+LlwDNCLLpkrTS5xDpIVeyQLPQU3YWXzm7edyBpl5yfEch41G8FF8yOECAwEAAaOCA1gwggNUMA4GA1UdDwEB/wQEAwIFoDCBlgYIKwYBBQUHAQEEgYkwgYYwRwYIKwYBBQUHMAKGO2h0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzZXh0ZW5kdmFsc2hhMmczcjMuY3J0MDsGCCsGAQUFBzABhi9odHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vZ3NleHRlbmR2YWxzaGEyZzNyMzBVBgNVHSAETjBMMEEGCSsGAQQBoDIBATA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAHBgVngQwBATAJBgNVHRMEAjAAMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vZ3MvZ3NleHRlbmR2YWxzaGEyZzNyMy5jcmwwHwYDVR0RBBgwFoIUbWRzLmZpZG9hbGxpYW5jZS5vcmcwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFN2z522oLujFTm7PdOZ1PJQVzugdMB0GA1UdDgQWBBRpe3o8CXqUpGc5UIQcjPQI+DIqwDCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHUAb1N2rDHwMRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RMAAAF4x6kCmAAABAMARjBEAiASzKclTD/rRitj03qplSoGH9aysUC3VwTEU/0qV6WgWQIgdqtGW4CBpCYx1y97ws2XmEIBmcd3x/5UxHGx9fT97G8AdwApeb7wnjk5IfBWc59jpXflvld9nGAK+PlNXSZcJV3HhAAAAXjHqQKQAAAEAwBIMEYCIQDGVmFP56wxu0TAYhFZ+VmsFjao2qAOqJ0c5/5L14q/OAIhAOvgXOHBGdhj5LerGsJfSm3T5yqvLcMXPPJl0njf1HE1AHYAUaOw9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF4x6kCwgAABAMARzBFAiEA3E+koSd7jyrsbc92x4Q2GV4I1eHGU7G64DW6s1FEDtUCIHGcrbbyCQG+tbirbMyW00elN6zQyhcWM2azF0E2wIPDMA0GCSqGSIb3DQEBCwUAA4IBAQCcCsxyd4GWWZ4GrCJX5A8UUqvstT+pGxhXQq0QPyTMQMXQm2EOgPRPz/H3lkrKf0W9DBhldjDRTm9CrahhIlFiXRrkstv5P484kxUpotUQrt1Wx0OmmNKNZmO3et5GF2TRTgiCRJ+s1z+3W4r9soxiAXJ7//MHEghwBTRGWsNN61pE/pN+/MSeWObXhTjshlW4RrIO2dvyHfu2Z+aMnbnmqRxQK5UxdtXSRQTZvRUTnCEEHFN5L6gaior+YRSJfN2qMnv/28kobA3UkoEusBSLeGrb7OU9lWbf7CeuNcN4n0umo+qpOnYOxzWsJm4xXtjZBvslHbh1dvZ1ivk2Vxin
-----END CERTIFICATE-----`; // 証明書B(中間証明書) "chain certificate"とも const certBStr
= `-----BEGIN CERTIFICATE-----
MIIEYTCCA0mgAwIBAgIOSKQC3SeSDaIINJ3RmXswDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTYwOTIxMDAwMDAwWhcNMjYwOTIxMDAwMDAwWjBiMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTE4MDYGA1UEAxMvR2xvYmFsU2lnbiBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrawNnVNXcEfvFohPBjBkn3BB04mGDPfqO24+lD+SpvkY/Ar5EpAkcJjOfR0iBFYhWN80HzpXYy2tIA7mbXpKu2JpmYdU1xcoQpQK0ujE/we+vEDyjyjmtf76LLqbOfuq3xZbSqUqAY+MOvA67nnpdawvkHgJBFVPnxui45XH4BwTwbtDucx+Mo7EK4mS0Ti+P1NzARxFNCUFM8Wxc32wxXKff6WU4TbqUx/UJm485ttkFqu0Ox4wTUUbn0uuzK7yV3Y986EtGzhKBraMH36MekSYlE473GqHetRi9qbNG5pM++Sa+WjR9E1e0Yws16CGqsmVKwAqg4uc43eBTFUhVAgMBAAGjggEpMIIBJTAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU3bPnbagu6MVObs905nU8lBXO6B0wHwYDVR0jBBgwFoAUj/BLf6guRSSuTVD6Y5qL3uLdG7wwPgYIKwYBBQUHAQEEMjAwMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vcm9vdHIzMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQBVaJzl0J/i0zUV38iMXIQ+Q/yht+JZZ5DW1otGL5OYV0LZ6ZE6xh+WuvWJJ4hrDbhfo6khUEaFtRUnurqzutvVyWgW8msnoP0gtMZO11cwPUMUuUV8iGyIOuIB0flo6G+XbV74SZuR5v5RAgqgGXucYUPZWvv9AfzMMQhRQkr/MO/WR2XSdiBrXHoDL2xk4DmjA4K6iPI+1+qMhyrkUM/2ZEdA8ldqwl8nQDkKS7vq6sUZ5LPVdfpxJZZu5JBj4y7FNFTVW1OMlCUvwt5H8aFgBMLFik9xqK6JFHpYxYmf4t2sLLxN0LlCthJEabvp10ZlOtfu8hL5gCXcxnwGxzSb
-----END CERTIFICATE-----`; // ルート証明書 "root certificate"とも const certRootStr =
`-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH WD9f -----END
CERTIFICATE-----`; /** * PEM形式の証明書チェーンの配列を受け取り、それを検証する * @param {String[]}
certificates - PEM形式の証明書チェーン(後方がより上位、最後尾がルート証明書) * @return {Boolean} -
証明書チェーンが正当ならばtrueを返す */ const verifyCertificateChain = (certificates) => { const
rootCertPEM = certificates[certificates.length - 1]; const rootCert = new
jsrsasign.X509(); rootCert.readCertPEM(rootCertPEM); let valid = true; for(let i
= 0; i < certificates.length - 1; i++) { // ルート証明書には更に上位の証明書は存在しないため、ここでは検証しない
const cert = certificates[i]; const certificate = new jsrsasign.X509();
certificate.readCertPEM(cert); const certStruct =
jsrsasign.ASN1HEX.getTLVbyList(certificate.hex, 0, [0]); const algorithm =
certificate.getSignatureAlgorithmField(); const signatureHex =
certificate.getSignatureValueHex() // 上位の証明書に対して検証をおこなう const signature = new
jsrsasign.crypto.Signature({alg: algorithm}); const upperCertPEM =
certificates[i + 1]; signature.init(upperCertPEM);
signature.updateHex(certStruct); valid = valid &&
signature.verify(signatureHex); // チェーン全ての証明書が正当かを確認 } return valid; } const
pemCertificateChain = [ certAStr, certBStr, certRootStr, ]; const result =
verifyCertificateChain(pemCertificateChain); if (result) {
console.log("証明書チェーンの検証成功!"); } else { console.log("証明書チェーンの検証失敗"); }

view raw node_certification_chain_jsrsasign.js hosted with ❤ by GitHub

下にも同じコードを置きました。

nodejs-module-labo/verify-certificate-chain.js at main ·
s1r-J/nodejs-module-labo


おわり

jsrsasignのほうがX509証明書から拡張設定の情報を確認したり、JWSの検証ができたりと使いたい機能があって最近よく使っています。また、ドキュメント(jsrsasign
JavaScript API Reference)が充実しています。

証明書の有効期限を確認するNotBeforeとNotAfterの返却値の形式がちょっと慣れていない形式(RFC5280で定義されている形式)なので戸惑いました。
RFC5280では時間をYYMMDDHHMMSSZという形式で表します。つまり、UTC時刻で2022年1月29日12時34分56秒は220129123456Zとなります。これについては、jsrsasignから使いやすいUNIX時刻(秒)に変換できるzulutosecというメソッドが用意されています。同じくミリ秒またはJSのDateオブジェクトに変換するメソッドも用意されています。

最後に前回と同じく注意点ですが、実装例の証明書は有効期限や何らかの要因で使えなくなる可能性があります。
その際は、何かしら別の証明書チェーンの証明書に差し替えてお試しください。

Node.js 証明書 X.509 jsrsasign

s1r-J 107日前




広告を非表示にする

 * もっと読む

コメントを書く
2021-12-27


FIDO2サーバ実装に必要そうな仕様書「SERVER REQUIREMENTS AND TRANSPORT BINDING PROFILE」を日本語訳する

FIDO 認証・認可 日本語訳

Server Requirements and Transport Binding Profileの日本語訳をしたので残しておきます。

W3CのWebAuthnに関する仕様書(Web Authentication: An API for accessing Public Key
Credentials - Level
2)と合わせて、FIDO2サーバの実装に重要な仕様書のはず。WebAuthnはリライングパーティ向けなのでFIDO2サーバの実装にはこちらが重要なのか?この仕様書でもWebAuthnと競合する場合にはこちらのドキュメントが優先される旨、FIDO2
Conformance Test(FIDO2サーバの認定を受けるために必要な適合性試験)についての言及があるので、確認が必要なはず。
FIDOアライアンスのドキュメント一覧のページからはリンクがないなど、このドキュメントの位置付けがよくわからない。

—

Server Requirements and Transport Binding Profile

Review Draft,July 02, 2018


概要

FIDO2は、Client-to-Authenticator Protocol (CTAP)を実装した認証器およびプアラットフォームやW3C
WebAuthn仕様書を実装したブラウザの利用を通じてセキュアな認証を提供する。これらの認証器は、登録および認証要求の正当性を確認するサーバと通信することが期待されます。FIDO2サーバに対する多くの必須要件、アサーションフォーマットやアテステーションフォーマットやオプショナルな拡張機能などはW3C
WebAuthn仕様書に含まれています。サーバの必須要件およびガイダンスに関する本仕様書は、サーバに関係のない認証器およびWebブラウザの詳細を残しつつ、FIDO2サーバの実装に役立てられるようにサーバに関する全ての要件を1つのドキュメントにまとめようとしている。


1 イントロダクション

この仕様書は、サーバ実装に関してW3C
WebAuthnの仕様書からの情報を取り込んだ必須要件とガイダンスを提供する。サーバは、実装が連携して機能することを保証することからFIDOエコシステムにおいて非常に重要である。異なるアテステーションフォーマット(packed、Android、TPMなど)、アテステーションモード(サロゲート、フル、ECDAAなど)、暗号化アルゴリズム(RSA、ECDSAなど)というように、様々な仕様書に多くのオプション機能がある。これらの多様な機能を実装している認証器は、一般的にはメモリおよびまたはCPUに制限があり、先述のような様々な機能を実装することに制限がある消費者向けの電子機器である。したがって、サーバは可能な限り幅広い種類の認証器と互換性を持つことを保証するため、これらの機能を可能な限り多く実装することになる。

WebAuthnの仕様書の概念は非常にシンプルである:サーバに新しい認証器を登録するメソッドを提供し(navigator.credentials.create())、登録済の認証器を利用した認証をおこなう別のメソッドを提供している(navigator.credentials.get())。登録において、認証器はその製造時に認証器に組み込んだアテステーション秘密鍵を利用してアテステーションステートメントを作成し、登録プロセスでのroot-of-trustを提供する。登録処理で登録されたアカウントごとに新しいキーペアを作成され、登録で作成した秘密鍵は正当な認証であることを示すためサーバに送信するアサーションに対して署名する用途に利用される。以下のセクションでは登録とアテステーションの必須要件および認証とアサーションの必須要件について記述している。

サーバには特定の必須プロトコル(REST、SOAP、伝書鳩、量子テレポーテーションなど)は存在しない(ただし、セキュアなコミュニケーションチャンネルに関する要件は存在する)。サーバはブラウザ、プラットフォーム、認証器が作成するによって作成された何らかの形式のJavaScriptオブジェクトを受け取っていることが想定される。これらのオブジェクトは署名されているため、プロトコルは署名されたオブジェクトをその署名が不当となるような方法で変更してはならない(MUST
NOT)が、そうではない場合はサーバにこれらのオブジェクトを送る任意の形式が利用できる。以下の必須要件とガイダンスは、これらのオブジェクトをサーバに対して送受信する方法に関する要件について定めていない。

この仕様書とWebAuthnの仕様書が競合する場合、WebAuthn仕様書が優先される。しかし、この仕様書がWebAuthn仕様書よりも優先とする説明や追記があり、WebブラウザにおいてWebAuthnを実装する方法についての多くの記述はサーバ実装には無関係である。


2 登録とアテステーション

サーバは登録をサポートするものとする(SHALL)。登録リクエストは、チャレンジを認証器に送信してレスポンスにCredentialCreationOptionsオブジェクト(または類似のもの)を受け取るという形を取る。PublicKeyCredentialのresponse属性はシリアライズされたclientDataJSON属性およびシリアライズされたattestationObject属性の両方を有している。シリアライズ形式(例
base64urlエンコーディング)には、デシリアライズされたときに元のバイト構造がアテステーションにおいて署名されたものと同じであることという要件を除き、他の要件は存在しない。

サーバは登録リクエストごとにランダムなチャレンジを利用するものとする(SHALL)。チャレンジのランダム性の定義はこの仕様書の範囲外(詳細については[FIDOSecRef]を参照)ではあるが、同じチャレンジや単調増加するチャレンジ、その他単純なチャンレジを利用することは受け入れられず安全ではない。チャレンジの生成には暗号論的乱数生成器を利用することが期待される。


2.1 アテステーションの検証

サーバはアテステーションを検証するものとする(SHALL)。[WebAuthn]はアテステーションの検証方法を指定している。Relying
Partyに対する要件はサーバにも標準である。AttestationResponseのフィールド名は[WebAuthn]仕様書のフィールド名および形式と一致しなくてもよい(MAY
NOT)ことに注意する。アプリケーションとサーバが独自のフィールド名、形式で通信する可能性がある。[WebAuthn]に記載されている名前および形式は便宜上のものである。

サーバはアテステーション証明書チェーンを検証するものとする(SHALL)。

サーバはFIDO Metadata Service
[FIDOMetadataService]を利用してアテステーションの検証をサポートしなければならない(MUST)。

サーバは、認証器のメタデータ属性に基づいて、認証器の許可・不許可、追加の認証要素の要求、リスク分析の実施に関するポリシーを持ってもよい(MAY)。


2.2 アテステーションタイプ

[WebAuthn]では複数のアテステーションタイプが定義されている。サーバはアテステーションフォーマットのうち1つをサポートしなければならない(MUST)。

 * サーバはbasicアテステーションをサポートしなければならない(MUST)
 * サーバはselfアテステーションをサポートしなければならない(MUST)
 * サーバはPrivacy CAアテステーションをサポートしてもよい(MAY)
 * サーバはElliptic Curve Direct Anonymousアテステーション(ECDAA)をサポートしてもよい(MAY)


2.3 アテステーションフォーマット

[WebAuthn]は複数のアテステーションフォーマットを定義しており、エコシステムの進歩に応じて[WebAuthn-Registries]レジストリに新しいアテステーションフォーマットが随時追加・更新される可能性がある。サーバは少なくとも1つのアテステーションフォーマットをサポートしなければならない(MUST)。

 * サーバはPackedアテステーションをサポートしなければならない(MUST):[WebAuthn]
 * サーバはTPMアテステーションをサポートしなければならない(MUST):[WebAuthn]
 * サーバはAndroid Keyアテステーションをサポートする必要がある(SHOULD):[WebAuthn]
 * サーバはU2Fアテステーションをサポートしなければならない(MUST):[WebAuthn]
 * サーバはAndroid SafteyNetアテステーションをサポートしなければならない(MUST):[WebAuthn]
 * サーバは、随時更新される[WebAuthn-Registries]に定義されている他のアテステーションフォーマットをサポートしてもよい(MAY)。認証器またはサーバが新しいアテステーションフォーマットを作成した場合、[WebAuthn-Registries]レジストリに登録する必要がある(SHOULD)。

2.3.1 PACKEDアテステーション

サーバは、[WebAuthn]に定義されている「Validation
Procedure」に従ってPackedアテステーションを検証しなければならない(MUST)。

EXAMPLE 1

{
    "rawId": "sL39APyTmisrjh11vghaqNfuruLQmCfR0c1ryKtaQ81jkEhNa5u9xLTnkibvXC9YpzBLFwWEZ3k9CR_sxzm_pWYbBOtKxeZu9z2GT8b6QW4iQvRlyumCT3oENx_8401r",
    "id": "sL39APyTmisrjh11vghaqNfuruLQmCfR0c1ryKtaQ81jkEhNa5u9xLTnkibvXC9YpzBLFwWEZ3k9CR_sxzm_pWYbBOtKxeZu9z2GT8b6QW4iQvRlyumCT3oENx_8401r",
    "response": {
        "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJ1Vlg4OElnUmEwU1NyTUlSVF9xN2NSY2RmZ2ZSQnhDZ25fcGtwVUFuWEpLMnpPYjMwN3dkMU9MWFEwQXVOYU10QlIzYW1rNkhZenAtX1Z4SlRQcHdHdyIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4ub3JnIiwidG9rZW5CaW5kaW5nIjp7InN0YXR1cyI6Im5vdC1zdXBwb3J0ZWQifSwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9",
        "attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEgwRgIhAIsK0Wr9tmud-waIYoQw20UWi7DL_gDx_PNG3PB57eHLAiEAtRyd-4JI2pCVX-dDz4mbHc_AkvC3d_4qnBBa3n2I_hVjeDVjg1kCRTCCAkEwggHooAMCAQICEBWfe8LNiRjxKGuTSPqfM-IwCgYIKoZIzj0EAwIwSTELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFEZlaXRpYW4gVGVjaG5vbG9naWVzMRswGQYDVQQDDBJGZWl0aWFuIEZJRE8yIENBLTEwIBcNMTgwNDExMDAwMDAwWhgPMjAzMzA0MTAyMzU5NTlaMG8xCzAJBgNVBAYTAkNOMR0wGwYDVQQKDBRGZWl0aWFuIFRlY2hub2xvZ2llczEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEdMBsGA1UEAwwURlQgQmlvUGFzcyBGSURPMiBVU0IwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASABnVcWfvJSbAVqNIKkliXvoMKsu_oLPiP7aCQlmPlSMcfEScFM7QkRnidTP7hAUOKlOmDPeIALC8qHddvTdtdo4GJMIGGMB0GA1UdDgQWBBR6VIJCgGLYiuevhJglxK-RqTSY8jAfBgNVHSMEGDAWgBRNO9jEZxUbuxPo84TYME-daRXAgzAMBgNVHRMBAf8EAjAAMBMGCysGAQQBguUcAgEBBAQDAgUgMCEGCysGAQQBguUcAQEEBBIEEEI4MkVENzNDOEZCNEU1QTIwCgYIKoZIzj0EAwIDRwAwRAIgJEtFo76I3LfgJaLGoxLP-4btvCdKIsEFLjFIUfDosIcCIDQav04cJPILGnPVPazCqfkVtBuyOmsBbx_v-ODn-JDAWQH_MIIB-zCCAaCgAwIBAgIQFZ97ws2JGPEoa5NI-p8z4TAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDQxMDAwMDAwMFoYDzIwMzgwNDA5MjM1OTU5WjBJMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxGzAZBgNVBAMMEkZlaXRpYW4gRklETzIgQ0EtMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI5-YAnswRZlzKD6w-lv5Qg7lW1XJRHrWzL01mc5V91n2LYXNR3_S7mA5gupuTO5mjQw8xfqIRMHVr1qB3TedY-jZjBkMB0GA1UdDgQWBBRNO9jEZxUbuxPo84TYME-daRXAgzAfBgNVHSMEGDAWgBTRoZhNgX_DuWv2B2e9UBL-kEXxVDASBgNVHRMBAf8ECDAGAQH_AgEAMA4GA1UdDwEB_wQEAwIBBjAKBggqhkjOPQQDAgNJADBGAiEA-3-j0kBHoRFQwnhWbSHMkBaY7KF_TztINFN5ymDkwmUCIQDrCkPBiMHXvYg-kSRgVsKwuVtYonRvC588qRwpLStZ7FkB3DCCAdgwggF-oAMCAQICEBWfe8LNiRjxKGuTSPqfM9YwCgYIKoZIzj0EAwIwSzELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFEZlaXRpYW4gVGVjaG5vbG9naWVzMR0wGwYDVQQDDBRGZWl0aWFuIEZJRE8gUm9vdCBDQTAgFw0xODA0MDEwMDAwMDBaGA8yMDQ4MDMzMTIzNTk1OVowSzELMAkGA1UEBhMCQ04xHTAbBgNVBAoMFEZlaXRpYW4gVGVjaG5vbG9naWVzMR0wGwYDVQQDDBRGZWl0aWFuIEZJRE8gUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJ3wCm47zF9RMtW-pPlkEHTVTLfSYBlsidz7zOAUiuV6k36PvtKAI_-LZ8MiC9BxQUfUrfpLY6klw344lwLq7POjQjBAMB0GA1UdDgQWBBTRoZhNgX_DuWv2B2e9UBL-kEXxVDAPBgNVHRMBAf8EBTADAQH_MA4GA1UdDwEB_wQEAwIBBjAKBggqhkjOPQQDAgNIADBFAiEAt7E9ZQYxnhfsSk6c1dSmFNnJGoU3eJiycs2DoWh7-IoCIA9iWJH8h-UOAaaPK66DtCLe6GIxdpIMv3kmd1PRpWqsaGF1dGhEYXRhWOSVaQiPHs7jIylUA129ENfK45EwWidRtVm7j9fLsim91EEAAAABQjgyRUQ3M0M4RkI0RTVBMgBgsL39APyTmisrjh11vghaqNfuruLQmCfR0c1ryKtaQ81jkEhNa5u9xLTnkibvXC9YpzBLFwWEZ3k9CR_sxzm_pWYbBOtKxeZu9z2GT8b6QW4iQvRlyumCT3oENx_8401rpQECAyYgASFYIFkdweEE6mWiIAYPDoKz3881Aoa4sn8zkTm0aPKKYBvdIlggtlG32lxrang8M0tojYJ36CL1VMv2pZSzqR_NfvG88bA"
    }
};


2.3.2 TPMアテステーション

サーバは、[WebAuthn]に定義されている「Validation Procedure」に従ってTPMアテステーションを検証しなければならない(MUST)。

EXAMPLE 2

{
    "rawId": "hWzdFiPbOMQ5KNBsMhs-Zeh8F0iTHrH63YKkrxJFgjQ",
    "id": "hWzdFiPbOMQ5KNBsMhs-Zeh8F0iTHrH63YKkrxJFgjQ",
    "response": {
        "clientDataJSON": "ew0KCSJ0eXBlIiA6ICJ3ZWJhdXRobi5jcmVhdGUiLA0KCSJjaGFsbGVuZ2UiIDogIndrNkxxRVhBTUFacHFjVFlsWTJ5b3I1RGppeUlfYjFneTluRE90Q0IxeUdZbm1fNFdHNFVrMjRGQXI3QXhUT0ZmUU1laWdrUnhPVExaTnJMeEN2Vl9RIiwNCgkib3JpZ2luIiA6ICJodHRwczovL3dlYmF1dGhuLm9yZyIsDQoJInRva2VuQmluZGluZyIgOiANCgl7DQoJCSJzdGF0dXMiIDogInN1cHBvcnRlZCINCgl9DQp9",
        "attestationObject": "o2NmbXRjdHBtaGF1dGhEYXRhWQFnlWkIjx7O4yMpVANdvRDXyuORMFonUbVZu4_Xy7IpvdRFAAAAAAiYcFjK3EuBtuEw3lDcvpYAIIVs3RYj2zjEOSjQbDIbPmXofBdIkx6x-t2CpK8SRYI0pAEDAzkBACBZAQDF2m9Nk1e94gL1xVjNCjFW0lTy4K2atXkx-YJrdH3hrE8p1gcIdNzleRDhmERJnY5CRwM5sXDQIrUBq4jpwvTtMC5HGccN6-iEJAPtm9_CJzCmGhtw9hbF8bcAys94RhN9xLLUaajhWqtPrYZXCEAi0o9E2QdTIxJrcAfJgZOf33JMr0--R1BAQxpOoGRDC8ss-tfQW9ufZLWw4JUuz4Z5Jz1sbfqBYB8UUDMWoT0HgsMaPmvd7T17xGvB-pvvDf-Dt96vFGtYLEZEgho8Yu26pr5CK_BOQ-2vX9N4MIYVPXNhogMGGmKYqybhM3yhye0GdBpZBUd5iOcgME6uGJ1_IUMBAAFnYXR0U3RtdKZjdmVyYzIuMGNhbGc5__5jc2lnWQEAcV1izWGUWIs0DEOZNQGdriNNXo6nbrGDLzEAeswCK9njYGCLmOkHVgSyafhsjCEMZkQmuPUmEOMDKosqxup_tiXQwG4yCW9TyWoINWGayQ4vcr6Ys-l6KMPkg__d2VywhfonnTJDBfE_4BIRD60GR0qBzTarthDHQFMqRtoUtuOsTF5jedU3EQPojRA5iCNC2naCCZuMSURdlPmhlW5rAaRZVF41ZZECi5iFOM2rO0UpGuQSLUvr1MqQOsDytMf7qWZMvwT_5_8BF6GNdB2l2VzmIJBbV6g8z7dj0fRkjlCXBp8UG2LvTq5SsfugrRWXOJ8BkdMplPfl0mz6ssU_n2N4NWOCWQS2MIIEsjCCA5qgAwIBAgIQEyidpWZzRxOSMNfrAvV1fzANBgkqhkiG9w0BAQsFADBBMT8wPQYDVQQDEzZOQ1UtTlRDLUtFWUlELTE1OTFENEI2RUFGOThEMDEwNDg2NEI2OTAzQTQ4REQwMDI2MDc3RDMwHhcNMTgwNTIwMTYyMDQ0WhcNMjgwNTIwMTYyMDQ0WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvQ6XK2ujM11E7x4SL34p252ncyQTd3-4r5ALQhBbFKS95gUsuENTG-48GBQwu48i06cckm3eH20TUeJvn4-pj6i8LFOrIK14T3P3GFzbxgQLq1KVm63JWDdEXk789JgzQjHNO7DZFKWTEiktwmBUPUA88TjQcXOtrR5EXTrt1FzGzabOepFann3Ny_XtxI8lDZ3QLwPLJfmk7puGtkGNaXOsRC7GLAnoEB7UWvjiyKG6HAtvVTgxcW5OQnHFb9AHycU5QdukXrP0njdCpLCRR0Nq6VMKmVU3MaGh-DCwYEB32sPNPdDkPDWyk16ItwcmXqfSBV5ZOr8ifvcXbCWUWwIDAQABo4IB5TCCAeEwDgYDVR0PAQH_BAQDAgeAMAwGA1UdEwEB_wQCMAAwbQYDVR0gAQH_BGMwYTBfBgkrBgEEAYI3FR8wUjBQBggrBgEFBQcCAjBEHkIAVABDAFAAQQAgACAAVAByAHUAcwB0AGUAZAAgACAAUABsAGEAdABmAG8AcgBtACAAIABJAGQAZQBuAHQAaQB0AHkwEAYDVR0lBAkwBwYFZ4EFCAMwSgYDVR0RAQH_BEAwPqQ8MDoxODAOBgVngQUCAwwFaWQ6MTMwEAYFZ4EFAgIMB05QQ1Q2eHgwFAYFZ4EFAgEMC2lkOjRFNTQ0MzAwMB8GA1UdIwQYMBaAFMISqVvO-lb4wMFvsVvdAzRHs3qjMB0GA1UdDgQWBBSv4kXTSA8i3NUM0q57lrWpM8p_4TCBswYIKwYBBQUHAQEEgaYwgaMwgaAGCCsGAQUFBzAChoGTaHR0cHM6Ly9hemNzcHJvZG5jdWFpa3B1Ymxpc2guYmxvYi5jb3JlLndpbmRvd3MubmV0L25jdS1udGMta2V5aWQtMTU5MWQ0YjZlYWY5OGQwMTA0ODY0YjY5MDNhNDhkZDAwMjYwNzdkMy8zYjkxOGFlNC0wN2UxLTQwNTktOTQ5MS0wYWQyNDgxOTA4MTguY2VyMA0GCSqGSIb3DQEBCwUAA4IBAQAs-vqdkDX09fNNYqzbv3Lh0vl6RgGpPGl-MYgO8Lg1I9UKvEUaaUHm845ABS8m7r9p22RCWO6TSEPS0YUYzAsNuiKiGVna4nB9JWZaV9GDS6aMD0nJ8kNciorDsV60j0Yb592kv1VkOKlbTF7-Z10jaapx0CqhxEIUzEBb8y9Pa8oOaQf8ORhDHZp-mbn_W8rUzXSDS0rFbWKaW4tGpVoKGRH-f9vIeXxGlxVS0wqqRm_r-h1aZInta0OOiL_S4367gZyeLL3eUnzdd-eYySYn2XINPbVacK8ZifdsLMwiNtz5uM1jbqpEn2UoB3Hcdn0hc12jTLPWFfg7GiKQ0hk9WQXsMIIF6DCCA9CgAwIBAgITMwAAAQDiBsSROVGXhwAAAAABADANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE2MDQGA1UEAxMtTWljcm9zb2Z0IFRQTSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDE0MB4XDTE3MDIwMTE3NDAyNFoXDTI5MTIzMTE3NDAyNFowQTE_MD0GA1UEAxM2TkNVLU5UQy1LRVlJRC0xNTkxRDRCNkVBRjk4RDAxMDQ4NjRCNjkwM0E0OEREMDAyNjA3N0QzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9IwUMSiQUbrQR0NLkKR-9RB8zfHYdlmDB0XN_m8qrNHKRJ__lBOR-mwU_h3MFRZF6X3ZZwka1DtwBdzLFV8lVu33bc15stjSd6B22HRRKQ3sIns5AYQxg0eX2PtWCJuIhxdM_jDjP2hq9Yvx-ibt1IO9UZwj83NGxXc7Gk2UvCs9lcFSp6U8zzl5fGFCKYcxIKH0qbPrzjlyVyZTKwGGSTeoMMEdsZiq-m_xIcrehYuHg-FAVaPLLTblS1h5cu80-ruFUm5Xzl61YjVU9tAV_Y4joAsJ5QP3VPocFhr5YVsBVYBiBcQtr5JFdJXZWWEgYcFLdAFUk8nJERS7-5xLuQIDAQABo4IBizCCAYcwCwYDVR0PBAQDAgGGMBsGA1UdJQQUMBIGCSsGAQQBgjcVJAYFZ4EFCAMwFgYDVR0gBA8wDTALBgkrBgEEAYI3FR8wEgYDVR0TAQH_BAgwBgEB_wIBADAdBgNVHQ4EFgQUwhKpW876VvjAwW-xW90DNEezeqMwHwYDVR0jBBgwFoAUeowKzi9IYhfilNGuVcFS7HF0pFYwcAYDVR0fBGkwZzBloGOgYYZfaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVFBNJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAxNC5jcmwwfQYIKwYBBQUHAQEEcTBvMG0GCCsGAQUFBzAChmFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRQTSUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMTQuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQAKc9z1UUBAaybIVnK8yL1N1iGJFFFFw_PpkxW76hgQhUcCxNFQskfahfFzkBD05odVC1DKyk2PyOle0G86FCmZiJa14MtKNsiu66nVqk2hr8iIcu-cYEsgb446yIGd1NblQKA1C_28F2KHm8YRgcFtRSkWEMuDiVMa0HDU8aI6ZHO04Naj86nXeULJSZsA0pQwNJ04-QJP3MFQzxQ7md6D-pCx-LVA-WUdGxT1ofaO5NFxq0XjubnZwRjQazy_m93dKWp19tbBzTUKImgUKLYGcdmVWXAxUrkxHN2FbZGOYWfmE2TGQXS2Z-g4YAQo1PleyOav3HNB8ti7u5HpI3t9a73xuECy2gFcZQ24DJuBaQe4mU5I_hPiAa-822nPPL6w8m1eegxhHf7ziRW_hW8s1cvAZZ5Jpev96zL_zRv34MsRWhKwLbu2oOCSEYYh8D8DbQZjmsxlUYR_q1cP8JKiIo6NNJ85g7sjTZgXxeanA9wZwqwJB-P98VdVslC17PmVu0RHOqRtxrht7OFT7Z10ecz0tj9ODXrv5nmBktmbgHRirRMl84wp7-PJhTXdHbxZv-OoL4HP6FxyDbHxLB7QmR4-VoEZN0vsybb1A8KEj2pkNY_tmxHH6k87euM99bB8FHrW9FNrXCGL1p6-PYtiky52a5YQZGT8Hz-ZnxobTmhjZXJ0SW5mb1ih_1RDR4AXACIAC7xZ9N_ZpqQtw7hmr_LfDRmCa78BS2erCtbrsXYwa4AHABSsnz8FacZi-wkUkfHu4xjG8MPfmwAAAAGxWkjHaED549jznwUBqeDEpT-7xBMAIgALcSGuv6a5r9BwMvQvCSXg7GdAjdWZpXv6D4DH8VYBCE8AIgALAVI0eQ_AAZjNvrhUEMK2q4wxuwIFOnHIDF0Qljhf47RncHViQXJlYVkBNgABAAsABgRyACCd_8vzbDg65pn7mGjcbcuJ1xU4hL4oA5IsEkFYv60irgAQABAIAAAAAAABAMXab02TV73iAvXFWM0KMVbSVPLgrZq1eTH5gmt0feGsTynWBwh03OV5EOGYREmdjkJHAzmxcNAitQGriOnC9O0wLkcZxw3r6IQkA-2b38InMKYaG3D2FsXxtwDKz3hGE33EstRpqOFaq0-thlcIQCLSj0TZB1MjEmtwB8mBk5_fckyvT75HUEBDGk6gZEMLyyz619Bb259ktbDglS7PhnknPWxt-oFgHxRQMxahPQeCwxo-a93tPXvEa8H6m-8N_4O33q8Ua1gsRkSCGjxi7bqmvkIr8E5D7a9f03gwhhU9c2GiAwYaYpirJuEzfKHJ7QZ0GlkFR3mI5yAwTq4YnX8"
    }
};


2.3.3 ANDROID KEYアテステーション例

サーバは、[WebAuthn]に定義されている「Validation Procedure」に従ってAndroid
Keyアテステーションを検証する必要がある(SHOULD)。

ISSUE 1:Android Keyアテステーションの例が必要

Android Keyアテステーション例を記載する必要がある。

2.3.4 ANDROID SAFETYNETアテステーション例

サーバは、[WebAuthn]に定義されている「Validation Procedure」に従ってAndroid
SafetyNetアテステーションを検証しなければならない(MUST)。

EXAMPLE 3

{
    "rawId": "qCXEfJ-dEoBlWqIl0iq2p_gj13HSg7r_MA7xOcOiO8RkCrYNmQHIjV9yhZVASr87cUsflo7DNuuvGsnrlTl1ig",
    "id": "qCXEfJ-dEoBlWqIl0iq2p_gj13HSg7r_MA7xOcOiO8RkCrYNmQHIjV9yhZVASr87cUsflo7DNuuvGsnrlTl1ig",
    "response": {
        "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJEa1hCdWRCa2wzTzBlTUV5SGZBTVgxT2tRbHV4c2hjaW9WU3dITVJMUlhtd044SXJldHg3cWJ0MWx3Y0p4d0FxWUU0SUxTZjVwd3lHMEhXSWtEekVMUT09Iiwib3JpZ2luIjoid2ViYXV0aG4ub3JnIiwiaGFzaEFsZyI6IlNIQS0yNTYifQ",
        "attestationObject": "o2hhdXRoRGF0YVjElWkIjx7O4yMpVANdvRDXyuORMFonUbVZu4_Xy7IpvdRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQKglxHyfnRKAZVqiJdIqtqf4I9dx0oO6_zAO8TnDojvEZAq2DZkByI1fcoWVQEq_O3FLH5aOwzbrrxrJ65U5dYqlAQIDJiABIVggh5OJfYRDzVGIowKqU57AnoVjjdmmjGi9zlMkjAVV9DAiWCDr0iSi0viIKNPMTIdN28gWNmkcwOr6DQx66MPff3Odm2NmbXRxYW5kcm9pZC1zYWZldHluZXRnYXR0U3RtdKJjdmVyaDEyNjg1MDIzaHJlc3BvbnNlWRSnZXlKaGJHY2lPaUpTVXpJMU5pSXNJbmcxWXlJNld5Sk5TVWxGYVdwRFEwRXpTMmRCZDBsQ1FXZEpTVmxyV1c4MVJqQm5PRFpyZDBSUldVcExiMXBKYUhaalRrRlJSVXhDVVVGM1ZrUkZURTFCYTBkQk1WVkZRbWhOUTFaV1RYaElha0ZqUW1kT1ZrSkJiMVJHVldSMllqSmtjMXBUUWxWamJsWjZaRU5DVkZwWVNqSmhWMDVzWTNwRmJFMURUVWRCTVZWRlFYaE5ZMUl5T1haYU1uaHNTVVZzZFdSSFZubGliVll3U1VWR01XUkhhSFpqYld3d1pWTkNTRTE2UVdWR2R6QjRUbnBGZVUxRVVYaE5la1UwVGtST1lVWjNNSGhQUkVWNVRVUk5kMDFFUVhkTlJFSmhUVWQzZUVONlFVcENaMDVXUWtGWlZFRnNWbFJOVWsxM1JWRlpSRlpSVVVsRVFYQkVXVmQ0Y0ZwdE9YbGliV3hvVFZKWmQwWkJXVVJXVVZGSVJFRXhUbUl6Vm5Wa1IwWndZbWxDVjJGWFZqTk5VazEzUlZGWlJGWlJVVXRFUVhCSVlqSTVibUpIVldkVFZ6VnFUVkp6ZDBkUldVUldVVkZFUkVKS2FHUklVbXhqTTFGMVdWYzFhMk50T1hCYVF6VnFZakl3ZDJkblJXbE5RVEJIUTFOeFIxTkpZak5FVVVWQ1FWRlZRVUUwU1VKRWQwRjNaMmRGUzBGdlNVSkJVVU5WYWpoM1dXOVFhWGhMWW1KV09ITm5XV2QyVFZSbVdDdGtTWE5HVkU5clowdFBiR2hVTUdrd1ltTkVSbHBMTW5KUGVFcGFNblZUVEZOV2FGbDJhWEJhVGtVelNFcFJXWFYxV1hkR2FtbDVLM2xyWm1GMFFVZFRhbEo2UmpGaU16RjFORE12TjI5SE5XcE5hRE5UTXpkaGJIZHFWV0k0UTFkcFZIaHZhWEJXVDFsM1MwdDZkVlY1YTNGRlEzUnFiR2hLTkVGclYyRkVVeXRhZUV0RmNVOWhaVGwwYmtOblpVaHNiRnBGTDA5U1oyVk5ZWGd5V0U1RGIwZzJjM0pVUlZKamEzTnFlbHBhY2tGWGVFdHpaR1oyVm5KWVRucERVamxFZUZaQlUzVkpOa3g2ZDJnNFJGTnNNa1ZQYjJ0aWMyRnVXaXNyTDBweFRXVkJRa1ptVUhkcWVYZHlZakJ3Y2tWVmVUQndZV1ZXYzNWa0t6QndaV1Y0U3k4MUswVTJhM0JaUjBzMFdrc3libXR2Vmt4MVowVTFkR0ZJY2tGcU9ETlJLMUJQWW1KMlQzcFhZMFpyY0c1V1MzbHFielpMVVVGdFdEWlhTa0ZuVFVKQlFVZHFaMmRHUjAxSlNVSlJha0ZVUW1kT1ZraFRWVVZFUkVGTFFtZG5ja0puUlVaQ1VXTkVRVlJCWkVKblRsWklVa1ZGUm1wQlZXZG9TbWhrU0ZKc1l6TlJkVmxYTld0amJUbHdXa00xYW1JeU1IZGhRVmxKUzNkWlFrSlJWVWhCVVVWRldFUkNZVTFETUVkRFEzTkhRVkZWUmtKNlFVTm9hVVp2WkVoU2QwOXBPSFpqUjNSd1RHMWtkbUl5WTNaYU0wNTVUV2s1U0ZaR1RraFRWVVpJVFhrMWFtTnVVWGRMVVZsSlMzZFpRa0pSVlVoTlFVZEhTRmRvTUdSSVFUWk1lVGwyV1ROT2QweHVRbkpoVXpWdVlqSTVia3d3WkZWVk1HUktVVlZqZWsxQ01FZEJNVlZrUkdkUlYwSkNVVWM0U1hKUmRFWlNOa05WVTJ0cGEySXpZV2x0YzIweU5tTkNWRUZOUW1kT1ZraFNUVUpCWmpoRlFXcEJRVTFDT0VkQk1WVmtTWGRSV1UxQ1lVRkdTR1pEZFVaRFlWb3pXakp6VXpORGFIUkRSRzlJTm0xbWNuQk1UVU5GUjBFeFZXUkpRVkZoVFVKbmQwUkJXVXRMZDFsQ1FrRklWMlZSU1VaQmVrRkpRbWRhYm1kUmQwSkJaMGwzVFZGWlJGWlNNR1pDUTI5M1MwUkJiVzlEVTJkSmIxbG5ZVWhTTUdORWIzWk1NazU1WWtNMWQyRXlhM1ZhTWpsMlduazVTRlpHVGtoVFZVWklUWGsxYW1OdGQzZEVVVmxLUzI5YVNXaDJZMDVCVVVWTVFsRkJSR2RuUlVKQlJpOVNlazV1UXpWRWVrSlZRblJ1YURKdWRFcE1WMFZSYURsNlJXVkdXbVpRVERsUmIydHliRUZ2V0dkcVYyZE9PSEJUVWxVeGJGWkhTWEIwZWsxNFIyaDVNeTlQVWxKYVZHRTJSREpFZVRob2RrTkVja1pKTXl0c1Exa3dNVTFNTlZFMldFNUZOVkp6TW1ReFVtbGFjRTF6ZWtRMFMxRmFUa2N6YUZvd1FrWk9VUzlqYW5KRGJVeENUMGRMYTBWVk1XUnRRVmh6UmtwWVNtbFBjakpEVGxSQ1QxUjFPVVZpVEZkb1VXWmtRMFl4WW5kNmVYVXJWelppVVZOMk9GRkVialZQWkUxVEwxQnhSVEZrUldkbGRDODJSVWxTUWpjMk1VdG1XbEVyTDBSRk5reHdNMVJ5V2xSd1QwWkVSR2RZYUN0TVowZFBjM2RvUld4cU9XTXpkbHBJUjBwdWFHcHdkRGh5YTJKcGNpOHlkVXhIWm5oc1ZsbzBTekY0TlVSU1RqQlFWVXhrT1hsUVUyMXFaeXRoYWpFcmRFaDNTVEZ0VVcxYVZsazNjWFpQTlVSbmFFOTRhRXBOUjJ4Nk5teE1hVnB0ZW05blBTSXNJazFKU1VWWVJFTkRRVEJUWjBGM1NVSkJaMGxPUVdWUGNFMUNlamhqWjFrMFVEVndWRWhVUVU1Q1oydHhhR3RwUnpsM01FSkJVWE5HUVVSQ1RVMVRRWGRJWjFsRVZsRlJURVY0WkVoaVJ6bHBXVmQ0VkdGWFpIVkpSa3AyWWpOUloxRXdSV2RNVTBKVFRXcEZWRTFDUlVkQk1WVkZRMmhOUzFJeWVIWlpiVVp6VlRKc2JtSnFSVlJOUWtWSFFURlZSVUY0VFV0U01uaDJXVzFHYzFVeWJHNWlha0ZsUm5jd2VFNTZRVEpOVkZWM1RVUkJkMDVFU21GR2R6QjVUVlJGZVUxVVZYZE5SRUYzVGtSS1lVMUdVWGhEZWtGS1FtZE9Wa0pCV1ZSQmJGWlVUVkkwZDBoQldVUldVVkZMUlhoV1NHSXlPVzVpUjFWblZraEtNV016VVdkVk1sWjVaRzFzYWxwWVRYaEtWRUZxUW1kT1ZrSkJUVlJJUldSMllqSmtjMXBUUWtwaWJsSnNZMjAxYkdSRFFrSmtXRkp2WWpOS2NHUklhMmRTZWsxM1oyZEZhVTFCTUVkRFUzRkhVMGxpTTBSUlJVSkJVVlZCUVRSSlFrUjNRWGRuWjBWTFFXOUpRa0ZSUkV0VmEzWnhTSFl2VDBwSGRXOHlia2xaWVU1V1YxaFJOVWxYYVRBeFExaGFZWG8yVkVsSVRFZHdMMnhQU2lzMk1EQXZOR2hpYmpkMmJqWkJRVUl6UkZaNlpGRlBkSE0zUnpWd1NEQnlTbTV1VDBaVlFVczNNVWMwYm5wTFRXWklRMGRWYTNOWEwyMXZibUVyV1RKbGJVcFJNazRyWVdsamQwcExaWFJRUzFKVFNXZEJkVkJQUWpaQllXaG9PRWhpTWxoUE0yZzVVbFZyTWxRd1NFNXZkVUl5Vm5wNGIwMVliR3Q1VnpkWVZWSTFiWGMyU210TVNHNUJOVEpZUkZadlVsUlhhMDUwZVRWdlEwbE9USFpIYlc1U2Mwb3hlbTkxUVhGWlIxWlJUV012TjNONUt5OUZXV2hCVEhKV1NrVkJPRXRpZEhsWUszSTRjMjUzVlRWRE1XaFZjbmRoVnpaTlYwOUJVbUU0Y1VKd1RsRmpWMVJyWVVsbGIxbDJlUzl6UjBsS1JXMXFVakIyUmtWM1NHUndNV05UWVZkSmNqWXZOR2MzTW00M1QzRllkMlpwYm5VM1dsbFhPVGRGWm05UFUxRktaVUY2UVdkTlFrRkJSMnBuWjBWNlRVbEpRa3g2UVU5Q1owNVdTRkU0UWtGbU9FVkNRVTFEUVZsWmQwaFJXVVJXVWpCc1FrSlpkMFpCV1VsTGQxbENRbEZWU0VGM1JVZERRM05IUVZGVlJrSjNUVU5OUWtsSFFURlZaRVYzUlVJdmQxRkpUVUZaUWtGbU9FTkJVVUYzU0ZGWlJGWlNNRTlDUWxsRlJraG1RM1ZHUTJGYU0xb3ljMU16UTJoMFEwUnZTRFp0Wm5Kd1RFMUNPRWRCTVZWa1NYZFJXVTFDWVVGR1NuWnBRakZrYmtoQ04wRmhaMkpsVjJKVFlVeGtMMk5IV1ZsMVRVUlZSME5EYzBkQlVWVkdRbmRGUWtKRGEzZEtla0ZzUW1kbmNrSm5SVVpDVVdOM1FWbFpXbUZJVWpCalJHOTJUREk1YW1NelFYVmpSM1J3VEcxa2RtSXlZM1phTTA1NVRXcEJlVUpuVGxaSVVqaEZTM3BCY0UxRFpXZEtZVUZxYUdsR2IyUklVbmRQYVRoMldUTktjMHh1UW5KaFV6VnVZakk1Ymt3eVpIcGpha2wyV2pOT2VVMXBOV3BqYlhkM1VIZFpSRlpTTUdkQ1JHZDNUbXBCTUVKbldtNW5VWGRDUVdkSmQwdHFRVzlDWjJkeVFtZEZSa0pSWTBOQlVsbGpZVWhTTUdOSVRUWk1lVGwzWVRKcmRWb3lPWFphZVRsNVdsaENkbU15YkRCaU0wbzFUSHBCVGtKbmEzRm9hMmxIT1hjd1FrRlJjMFpCUVU5RFFWRkZRVWhNWlVwc2RWSlVOMkoyY3pJMlozbEJXamh6YnpneGRISlZTVk5rTjA4ME5YTnJSRlZ0UVdkbE1XTnVlR2hITVZBeVkwNXRVM2hpVjNOdmFVTjBNbVYxZURsTVUwUXJVRUZxTWt4SldWSkdTRmN6TVM4MmVHOXBZekZyTkhSaVYxaHJSRU5xYVhJek4zaFVWRTV4VWtGTlVGVjVSbEpYVTJSMmRDdHViRkJ4ZDI1aU9FOWhNa2t2YldGVFNuVnJZM2hFYWs1VFpuQkVhQzlDWkRGc1drNW5aR1F2T0dOTVpITkZNeXQzZVhCMVprbzVkVmhQTVdsUmNHNW9PWHBpZFVaSmQzTkpUMDVIYkRGd00wRTRRMmQ0YTNGSkwxVkJhV2d6U21GSFQzRmpjR05rWVVOSmVtdENZVkk1ZFZsUk1WZzBhekpXWnpWQlVGSk1iM1Y2Vm5rM1lUaEpWbXMyZDNWNU5uQnRLMVEzU0ZRMFRGazRhV0pUTlVaRldteG1RVVpNVTFjNFRuZHpWbm81VTBKTE1sWnhiakZPTUZCSlRXNDFlRUUyVGxwV1l6ZHZPRE0xUkV4QlJuTm9SVmRtUXpkVVNXVXpaejA5SWwxOS5leUp1YjI1alpTSTZJbXhYYTBscWVEZFBOSGxOY0ZaQlRtUjJVa1JZZVhWUFVrMUdiMjVWWWxaYWRUUXZXSGszU1hCMlpGSkJRVUZCUVVGQlFVRkJRVUZCUVVGQlFVRkJRVUZCUVVGQlFVRkJRVkZMWjJ4NFNIbG1ibEpMUVZwV2NXbEtaRWx4ZEhGbU5FazVaSGd3YjA4MkwzcEJUemhVYmtSdmFuWkZXa0Z4TWtSYWEwSjVTVEZtWTI5WFZsRkZjUzlQTTBaTVNEVmhUM2Q2WW5KeWVISktOalZWTldSWmNXeEJVVWxFU21sQlFrbFdaMmRvTlU5S1psbFNSSHBXUjBsdmQwdHhWVFUzUVc1dlZtcHFaRzF0YWtkcE9YcHNUV3RxUVZaV09VUkJhVmREUkhJd2FWTnBNSFpwU1V0T1VFMVVTV1JPTWpoblYwNXRhMk4zVDNJMlJGRjROalpOVUdabU0wOWtiU3QxTm1WS2NVeENiREZJTWxNeWRISkJRa2hNYVc1cmJuTjVWazFRYlM5Q1RsVldXakpLUm14eU9EQWlMQ0owYVcxbGMzUmhiWEJOY3lJNk1UVXlPRGt4TVRZek5ETTROU3dpWVhCclVHRmphMkZuWlU1aGJXVWlPaUpqYjIwdVoyOXZaMnhsTG1GdVpISnZhV1F1WjIxeklpd2lZWEJyUkdsblpYTjBVMmhoTWpVMklqb2lTazlETTFWcmMyeHpkVlo2TVRObFQzQnVSa2s1UW5CTWIzRkNaemxyTVVZMlQyWmhVSFJDTDBkcVRUMGlMQ0pqZEhOUWNtOW1hV3hsVFdGMFkyZ2lPbVpoYkhObExDSmhjR3REWlhKMGFXWnBZMkYwWlVScFoyVnpkRk5vWVRJMU5pSTZXeUpIV0ZkNU9GaEdNM1pKYld3ekwwMW1ibTFUYlhsMVMwSndWRE5DTUdSWFlraFNVaTgwWTJkeEsyZEJQU0pkTENKaVlYTnBZMGx1ZEdWbmNtbDBlU0k2Wm1Gc2MyVXNJbUZrZG1salpTSTZJbEpGVTFSUFVrVmZWRTlmUmtGRFZFOVNXVjlTVDAwc1RFOURTMTlDVDA5VVRFOUJSRVZTSW4wLmlDRjZEMm9zOERZdURWT250M3pESkIybVNYblpqdFdKdGxfanpTRHg1TXJSQzlBMmZtRkJaNno1a3BRWjJNaVE3b290ajlXa0hNZ3hxSWhyWDNkbGgyUE9IQXdrSVMzNHlTakxWTnNTUHByRTg0ZVpncVNGTE1FWVQwR1IyZVZMSEFNUE44bjVSOEs2YnVET0dGM25TaTZHS3pHNTdabGw4Q1NvYjJ5aUFTOXI3c3BkQTZIMFRESC1OR3pTZGJNSUlkOGZaRDFkekZLTlFyNzdiNmxiSUFGZ1FiUlpCcm5wLWUtSDRpSDZkMjFvTjJOQVlSblI1WVVSYWNQNmtHR2oyY0Z4c3dFMjkwOHd4djloaVlOS05vamVldThYYzRJdDdQYmhsQXVPN3l3aFFGQTgxaVBDQ0ZtMTFCOGNmVVhiV0E4bF8ydHROUEJFTUdNNi1aNlZ5UQ"
    }
};


2.3.5 U2Fアテステーション

サーバは、[WebAuthn]に定義されている「Validation Procedure」に従ってU2Fアテステーションを検証しなければならない(MUST)。

EXAMPLE 4

{
    "rawId": "Bo-VjHOkJZy8DjnCJnIc0Oxt9QAz5upMdSJxNbd-GyAo6MNIvPBb9YsUlE0ZJaaWXtWH5FQyPS6bT_e698IirQ==",
    "id": "Bo-VjHOkJZy8DjnCJnIc0Oxt9QAz5upMdSJxNbd-GyAo6MNIvPBb9YsUlE0ZJaaWXtWH5FQyPS6bT_e698IirQ==",
    "response": {
        "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAO-683ISJhKdmUPmVbQuYZsp8lkD7YJcInHS3QOfbrioAiEAzgMJ499cBczBw826r1m55Jmd9mT4d1iEXYS8FbIn8MpjeDVjgVkCSDCCAkQwggEuoAMCAQICBFVivqAwCwYJKoZIhvcNAQELMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAqMSgwJgYDVQQDDB9ZdWJpY28gVTJGIEVFIFNlcmlhbCAxNDMyNTM0Njg4MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESzMfdz2BRLmZXL5FhVF-F1g6pHYjaVy-haxILIAZ8sm5RnrgRbDmbxMbLqMkPJH9pgLjGPP8XY0qerrnK9FDCaM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwCwYJKoZIhvcNAQELA4IBAQCsFtmzbrazqbdtdZSzT1n09z7byf3rKTXra0Ucq_QdJdPnFhTXRyYEynKleOMj7bdgBGhfBefRub4F226UQPrFz8kypsr66FKZdy7bAnggIDzUFB0-629qLOmeOVeAMmOrq41uxICn3whK0sunt9bXfJTD68CxZvlgV8r1_jpjHqJqQzdio2--z0z0RQliX9WvEEmqfIvHaJpmWemvXejw1ywoglF0xQ4Gq39qB5CDe22zKr_cvKg1y7sJDvHw2Z4Iab_p5WdkxCMObAV3KbAQ3g7F-czkyRwoJiGOqAgau5aRUewWclryqNled5W8qiJ6m5RDIMQnYZyq-FTZgpjXaGF1dGhEYXRhWMRJlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0EAAAAAAAAAAAAAAAAAAAAAAAAAAABABo-VjHOkJZy8DjnCJnIc0Oxt9QAz5upMdSJxNbd-GyAo6MNIvPBb9YsUlE0ZJaaWXtWH5FQyPS6bT_e698IiraUBAgMmIAEhWCA1c9AIeH5sN6x1Q-2qR7v255tkeGbWs0ECCDw35kJGBCJYIBjTUxruadjFFMnWlR5rPJr23sBJT9qexY9PCc9o8hmT",
        "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJWdTh1RHFua3dPamQ4M0tMajZTY24yQmdGTkxGYkdSN0txX1hKSndRbm5hdHp0VVI3WElCTDdLOHVNUENJYVFtS3cxTUNWUTVhYXpOSkZrN05ha2dxQSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0="
    }
};



3 認証とアサーション

サーバは認証をサポートするものとする(SHALL)。

サーバは認証リクエストごとにランダムなチャレンジを利用するものとする(SHALL)。チャレンジのランダム性の定義はこの仕様書の範囲外(詳細については[FIDOSecRef]を参照)ではあるが、同じチャレンジや単調増加するチャレンジ、その他単純なチャンレジを利用することは受け入れられず安全ではない。チャレンジの生成には暗号論的乱数生成器を利用することが期待される。

サーバはアサーション署名を検証するものとする(SHALL)。

アサーションレスポンスを受け取った際に、サーバは[WebAuthn]で定義されている手順でアサーションレスポンスを検証するものとする(SHALL)。

サーバはTUPおよびまたはその他ユーザ認証を検証するものとする(SHALL)。


4 コミュニケーションチャンネルの必須要件

サーバがTLSおよびトークンバインディングを利用できるように実装している場合、[TokenBindingOverHttp]を利用して[TokenBindingProtocol]を実装する必要がある(SHOULD).


5 拡張機能

サーバは、拡張機能が存在しなくても登録および認証を実行できる動作モードを用意しなければならない(MUST)。ただし、本番環境にデプロイするときにそのように構築する必要があるという要件はない。

サーバは拡張機能をサポートしてもよい(MAY)。

サーバは、FIDO
U2Fとの後方互換性のため、[WebAuthn]をサポートする必要がある(SHOULD)。ブラウザ、プラットフォームおよびその他クライアントが拡張機能をサポートしている可能性、サポートしていない可能性があることに注意する。

サーバが新しい拡張機能を実装した場合、[WebAuthn-Registries]レジストリに登録する必要がある(SHOULD)。


6 その他

[WebAuthn] セクション5.3.5のセキュリティ要件を遵守する必要がある。

signatureはrawData属性から算出される。

サーバは、以下にRequiredと記載されたアルゴリズムを実装しなければならず(MUST)、RecommendedおよびOptionalと記載されたアルゴリズムを実装してもよい(MAY)。また、サーバはその他アルゴリズムを実装しても良い(MAY)。

名称:RS1

 * :未定(-65535の割当を要求)
 * 説明:RSASSA-PKCS1-v1_5 w/ SHA-1
 * 参照:[RFC8017] セクション8.2
 * ステータス:Required

名称:RS256

 * :未定(-257の割当を要求)
 * 説明:RSASSA-PKCS1-v1_5 w/ SHA-256
 * 参照:[RFC8017] セクション8.2
 * ステータス:Required

名称:RS384

 * :未定(-258割当を要求)
 * 説明:RSASSA-PKCS1-v1_5 w/ SHA-384
 * 参照:[RFC8017] セクション8.2
 * ステータス:Optional

名称:RS512

 * :未定(-259の割当を要求)
 * 説明:RSASSA-PKCS1-v1_5 w/ SHA-512
 * 参照:[RFC8017] セクション8.2
 * ステータス:Optional

名称:PS256

 * :-37
 * 説明:RSASSA-PSS w/ SHA-256
 * 参照:[RFC8230]
 * ステータス:Optional

名称:PS384

 * :-38
 * 説明:RSASSA-PSS w/ SHA-384
 * 参照:[RFC8230]
 * ステータス:Optional

名称:PS512

 * :-39
 * 説明:RSASSA-PSS w/ SHA-512
 * 参照:[RFC8230]
 * ステータス:Optional

名称:ES256

 * :-7
 * 説明:ECDSA using P-256 and SHA-256
 * 参照:[RFC8152]
 * ステータス:Required

名称:ES384

 * :-35
 * 説明:ECDSA using P-384 and SHA-384
 * 参照:[RFC8152]
 * ステータス:Recommended

名称:ES512

 * :-36
 * 説明:ECDSA using P-512 and SHA-512
 * 参照:[RFC8152]
 * ステータス:Optional

名称:EdDSA

 * :-8
 * 説明:EdDSA signature algorithms
 * 参照:[RFC8037]
 * ステータス:Recommended

名称:ES256K

 * :未定(-43の割当を要求)
 * 説明:ECDSA using P-256K and SHA-256
 * 参照:[SEC2V2]
 * ステータス:Optional

サーバは、以下にRequiredと記載された曲線を実装しなければならず(MUST)、RecommendedおよびOptionalと記載された曲線を実装してもよい(MAY)。また、サーバはその他の曲線を実装しても良い(MAY)。

名称:P-256

 * :1
 * 説明:EC2 NIST P-256 also known as secp256r1
 * 参照:[RFC8152]
 * ステータス:Required

名称:P-384

 * :2
 * 説明:EC2 NIST P-384 also known as secp384r1
 * 参照:[RFC8152]
 * ステータス:Recommended

名称:P-512

 * :3
 * 説明:EC2 NIST P-512 also known as secp512r1
 * 参照:[RFC8152]
 * ステータス:Optional

名称:Ed25519

 * :6
 * 説明:Edwards-curve Digital Signature Algorithm on curve 25519
 * 参照: [RFC8032]
 * ステータス:Recommended

名称:Ed448

 * :7
 * 説明:Edwards-curve Digital Signature Algorithm on curve 448
 * 参照:[RFC8032]
 * ステータス:Optional

名称:P-256K

 * :未定(8の割当を要求)
 * 説明:SECG secp256k1 curve
 * 参照:[SEC2V2]
 * ステータス:Optional

書面の都合上、この仕様書を記述している時点で実際に認証器によって利用されているアルゴリズムおよび曲線だけが、Requiredのリストに含まれている。将来の暗号論開発の可能性に先行して備えたいサーバは、必須のアルゴリズムと曲線に加え、Recommendedとなっているアルゴリズムおよび曲線を実装することを検討するべきです。

サーバはFIDOプライバシー指針 [FIDOPrivacyPrinciples]を遵守しなければなりません(MUST)。


7 トランスポートバインディングプロフィール

このセクションは非標準です。


7.1 目次

 * イントロダクション
 * 登録
   * 概要
   * 例
     * Credential Creation Options
     * Authenticator Attestation Response
   * Primary IDL
     * ServerPublicKeyCredentialCreationOptionsRequest
     * ServerPublicKeyCredentialCreationOptionsResponse
     * ServerAuthenticatorAttestationResponse
   * Supporting IDL
     * ServerPublicKeyCredential
     * ServerPublicKeyCredentialUserEntity
     * ServerPublicKeyCredentialDescriptor
 * 認証
   * 概要
   * 例
     * Credential Get Options
     * Authenticator Assertion Response
   * IDL
     * ServerPublicKeyCredentialGetOptionsRequest
     * ServerPublicKeyCredentialGetOptionsResponse
     * ServerAuthenticatorAssertionResponse
 * 共通
   * IDL
     * ServerResponse


7.2 イントロダクション

このドキュメントは、FIDO2サーバ向けの非標準的なREST
APIを含む。このインタフェースは必須ではないが、FIDO2適合性テストツールには利用されているインタフェースであり、サーバは標準的な方法でメッセージを送受信してそれらメッセージが適合性テストツールによって検証される。

FIDO2仕様と同様に、ここで記述しているインタフェースは[WebAuthn]仕様に大きく依存している。このドキュメントの命名方法はWebAuthnに従っており、サーバと送受信するメッセージを定義するためにインタフェース定義言語(IDL)を再度利用している。

このドキュメントは、登録、認証そして共通という3つのセクションに分割されている。登録および認証のセクションではこれらの処理に関するメッセージが含まれており、共通セクションでは登録と認証の両方に共通するメッセージとデータフォーマットを含んでいる。


7.3 登録

このセクションは、クライアントとサーバ間で交換する登録メッセージの簡単な概要、それらメッセージの例とメッセージのIDL定義を記載している。WebAuthnの命名方法にしたがって「credential
creation」と参照されることもあるに注意する。

7.3.1 登録の概要

登録の手順は2つのステップになっており、4種類のメッセージが送信される。最初のステップでは、クライアントが「Credential Creation
Options」を受け取る。これにはクライアントがサーバに送ったServerPublicKeyCredentialCreationOptionsRequestとサーバが受け取ったServerPublicKeyCredentialCreationOptionsResponseを関連する。このはWebAuthnのnavigator.credentials.create()に利用されることを意図しており、特にサーバによって必ず生成されるチャレンジは中間者攻撃(MITM)対策のために重要である。navigator.credentials.create()の完了時に、呼び出し関数から生成されたディクショナリをServerAuthenticatorAttestationResponseをresponseフィールドに設定したServerPublicKeyCredentialとしてサーバに送り返す。ServerAuthenticatorAttestationResponseは以下の共通セクションに記述している一般的なServerAuthenticatorResponseを拡張していることに注意する。サーバはチャレンジ、オリジン、署名そして[WebAuthn]仕様書のセクション7.1に記載されているアルゴリズムに従ってServerAuthenticatorAttestationResponseのその他の属性を検証し、適切なServerResponseメッセージを返却する。

7.3.2 例

7.3.2.1 例:CREDENTIAL CREATION OPTIONS

リクエスト:

 * URL:/attestation/options
 * メソッド:POST
 * URLパラメータ:なし
 * ボディ:application/jsonフォーマットのServerPublicKeyCredentialCreationOptionsRequest

    {
        "username": "johndoe@example.com",
        "displayName": "John Doe",
        "authenticatorSelection": {
            "residentKey": false,
            "authenticatorAttachment": "cross-platform",
            "userVerification": "preferred"
        },
        "attestation": "direct"
    }


成功レスポンス:

 * HTTPステータスコード:200 OK
 * ボディ:application/jsonフォーマットのServerPublicKeyCredentialCreationOptionsResponse

    {
        "status": "ok",
        "errorMessage": "",
        "rp": {
            "name": "Example Corporation"
        },
        "user": {
            "id": "S3932ee31vKEC0JtJMIQ",
            "name": "johndoe@example.com",
            "displayName": "John Doe"
        },

        "challenge": "uhUjPNlZfvn7onwuhNdsLPkkE5Fv-lUN",
        "pubKeyCredParams": [
            {
                "type": "public-key",
                "alg": -7
            }
        ],
        "timeout": 10000,
        "excludeCredentials": [
            {
                "type": "public-key",
                "id": "opQf1WmYAa5aupUKJIQp"
            }
        ],
        "authenticatorSelection": {
            "residentKey": false,
            "authenticatorAttachment": "cross-platform",
            "userVerification": "preferred"
        },
        "attestation": "direct"
    }


エラーレスポンス:

 * HTTPステータスコード:4XXまたは5XX
 * ボディ:application/jsonフォーマットのServerResponse

    {
        "status": "failed",
        "errorMessage": "Missing challenge field!"
    }


JavaScriptサンプル:

    fetch('/attestation/options', {
        method  : 'POST',
        credentials : 'same-origin',
        headers : {
            'Content-Type' : 'application/json'
        },
        body: JSON.stringify({
            "username": "johndoe@example.com",
            "displayName": "John Doe",
            "authenticatorSelection": {
                "residentKey": false,
                "authenticatorAttachment": "cross-platform",
                "userVerification": "preferred"
            },
            "attestation": "direct"
        })
    }).then(function (response) {
        return response.json();
    }).then(function (json) {
        console.log(json);
    }).catch(function (err) {
        console.log({ 'status': 'failed', 'error': err });
    })


7.3.2.2 例:AUTHENTICATOR ATTESTAION RESPONSE

リクエスト:

 * URL:/attestation/result
 * メソッド:POST
 * URLパラメータ:なし
 * ボディ:application/jsonフォーマットのServerAuthenticatorAttestationResponseをresponseフィールドに設定したServerPublicKeyCredential

    {
        "id": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
        "rawId": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
        "response": {
            "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9",
            "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"
        },
        "type": "public-key"
    }


成功レスポンス:

 * HTTPステータスコード:200 OK
 * ボディ:application/jsonフォーマットのServerResponse

    {
        "status": "ok",
        "errorMessage": "",
    }


エラーレスポンス:

 * HTTPステータスコード:4XXまたは5XX
 * ボディ:application/jsonフォーマットのServerResponse

    {
        "status": "failed",
        "errorMessage": "Can not validate response signature!"
    }


呼び出しサンプル:

    fetch('/attestation/result', {
        method  : 'POST',
        credentials : 'same-origin',
        headers : {
            'Content-Type' : 'application/json'
        },
        body: JSON.stringify({
            "id": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
            "rawId": "LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
            "response": {
                "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJOeHlab3B3VktiRmw3RW5uTWFlXzVGbmlyN1FKN1FXcDFVRlVLakZIbGZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9",
                "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgVzzvX3Nyp_g9j9f2B-tPWy6puW01aZHI8RXjwqfDjtQCIQDLsdniGPO9iKr7tdgVV-FnBYhvzlZLG3u28rVt10YXfGN4NWOBWQJOMIICSjCCATKgAwIBAgIEVxb3wDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowLDEqMCgGA1UEAwwhWXViaWNvIFUyRiBFRSBTZXJpYWwgMjUwNTY5MjI2MTc2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZNkcVNbZV43TsGB4TEY21UijmDqvNSfO6y3G4ytnnjP86ehjFK28-FdSGy9MSZ-Ur3BVZb4iGVsptk5NrQ3QYqM7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAHibGMqbpNt2IOL4i4z96VEmbSoid9Xj--m2jJqg6RpqSOp1TO8L3lmEA22uf4uj_eZLUXYEw6EbLm11TUo3Ge-odpMPoODzBj9aTKC8oDFPfwWj6l1O3ZHTSma1XVyPqG4A579f3YAjfrPbgj404xJns0mqx5wkpxKlnoBKqo1rqSUmonencd4xanO_PHEfxU0iZif615Xk9E4bcANPCfz-OLfeKXiT-1msixwzz8XGvl2OTMJ_Sh9G9vhE-HjAcovcHfumcdoQh_WM445Za6Pyn9BZQV3FCqMviRR809sIATfU5lu86wu_5UGIGI7MFDEYeVGSqzpzh6mlcn8QSIZoYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAsV2gIUlPIHzZnNIlQdz5zvbKtpFz_WY-8ZfxOgTyy7f3Ffbolyp3fUtSQo5LfoUgBaBaXqK0wqqYO-u6FrrLApQECAyYgASFYIPr9-YH8DuBsOnaI3KJa0a39hyxh9LDtHErNvfQSyxQsIlgg4rAuQQ5uy4VXGFbkiAt0uwgJJodp-DymkoBcrGsLtkI"
            },
            "type": "public-key"
        })
    }).then(function (response) {
        return response.json();
    }).then(function (json) {
        console.log(json);
    }).catch(function (err) {
        console.log({ 'status': 'failed', 'error': err });
    })


7.3.3 登録PRIMARY IDL

7.3.3.1 SERVERPUBLICKEYCREDENTIALCREATIONOPTIONSREQUEST

    dictionary ServerPublicKeyCredentialCreationOptionsRequest {
        required DOMString              username;
        required DOMString              displayName;
        AuthenticatorSelectionCriteria  authenticatorSelection;
        AttestationConveyancePreference attestation = "none";
    };


 * 必須 username -
   エンティティについての人間が読むことができる名称。例えば「alexm」、「alex.p.mueller@example.com」や「+14255551234」
 * 必須 displayName - ユーザアカウントに対する人間にとって読みやすい名称、表示のためだけに利用されることを想定。例えば「Alex P.
   Müller」、「田中 倫」
 * authenticatorSelection -
   WebAuthn仕様書に記載されているAuthenticatorSelectionCriteriaを含むディクショナリ
 * attestation -
   「none」、「indirect」「direct」を設定することができる。更なる情報はWebAuthn仕様に記載されている。デフォルトは「none」が設定される。

7.3.3.2 SERVERPUBLICKEYCREDENTIALCREATIONOPTIONSRESPONSE

    dictionary ServerPublicKeyCredentialCreationOptionsResponse : ServerResponse {
        required PublicKeyCredentialRpEntity         rp;
        required ServerPublicKeyCredentialUserEntity user;

        required DOMString                                challenge;
        required sequence<PublicKeyCredentialParameters>  pubKeyCredParams;

        unsigned long                                 timeout;
        sequence<ServerPublicKeyCredentialDescriptor> excludeCredentials = [];
        AuthenticatorSelectionCriteria               authenticatorSelection;
        AttestationConveyancePreference              attestation = "none";
        AuthenticationExtensionsClientInputs         extensions;
    };


 * 必須 rp - WebAuthn仕様書に記述されているPublicKeyCredentialRpEntityとして定義されたディクショナリ
 * 必須 user - 本ドキュメントに記述したServerPublicKeyCredentialUserEntityとして定義されたディクショナリ
 * 必須 challenge - 最低16バイト長、最大64バイト長のランダムなチャレンジをbase64urlエンコードした値
 * 必須 pubKeyCredParams - WebAuthn仕様書に記述されたPublicKeyCredentialParametersの配列
 * timeout - タイムアウト(ms)
 * excludeCredentials - 本ドキュメントに記述されたServerPublicKeyCredentialDescriptorの配列
 * authenticatorSelection - WebAuthn仕様書に記述されたAuthenticatorSelectionCriteriaのセット
 * attestation -
   「none」、「indirect」「direct」を設定することができる。更なる情報はWebAuthn仕様に記載されている。デフォルトは「none」が設定される。
 * extensions - WebAuthn仕様書に記述された AuthenticationExtensionsClientInputsのセット
 * このドキュメントに記載したServerResponseを拡張している

7.3.3.3 SERVERAUTHENTICATORATTESTATIONRESPONSE

一般的にWebAuthnに記述されているAuthenticatorAttestationResponseと同等だが、BufferSource型のフィールドはbase64urlエンコーディングされて利用される。

dictionary ServerAuthenticatorAttestationResponse : ServerAuthenticatorResponse {
    required DOMString      clientDataJSON;
    required DOMString      attestationObject;
};


 * 必須 clientDataJSON - base64urlエンコードされたclientDataJSONバッファ
 * 必須 attestationObject - base64urlエンコードされたattestationObjectバッファ

7.3.4 登録 SUPPORTING IDL

7.3.4.1 SERVERPUBLICKEYCREDENTIAL

一般的にWebAuthnに記述されているPublicKeyCredentialと同等だが、WebAuthnでBufferSource型のフィールドはbase64urlエンコーディングされて利用される。

dictionary ServerPublicKeyCredential : Credential {
    required DOMString                    rawId;
    required ServerAuthenticatorResponse  response;
    AuthenticationExtensionsClientOutputs getClientExtensionResults;
};


 * 必須 id -
   この属性はCredentialから継承しているが、ServerPublicKeyCredentialでは認証器のcredIdのbase64urlエンコードされた値にオーバーライドされている
 * 必須 rawId - idと同等
 * 必須 response -
   本ドキュメントに記述したServerAuthenticatorAttestationResponseまたはServerAuthenticatorAssertionResponseによって定義されるディクショナリ
 * 必須 type -
   この属性はCredentialから継承しているが、ServerPublicKeyCredentialでは「public-key」にオーバーライドされている
 * getClientExtensionResult -
   拡張機能の識別子を含むマップ、拡張機能のクライアント拡張処理によって生成されたクライアント拡張出力エントリが含まれる
 * Credential Management API仕様書に記述されているCredentialを拡張している

7.3.4.2 SERVERPUBLICKEYCREDENTIALUSERENTITY

一般的にWebAuthnに記述されているPublicKeyCredentialUserEntityと同等だが、idのBufferSourceはbase64urlエンコーディングが代わりに利用される。

    dictionary ServerPublicKeyCredentialUserEntity : PublicKeyCredentialEntity {
        required DOMString   id;
        required DOMString   displayName;
    };


 * 必須 id - base64urlエンコードされたidバッファ
 * 必須 displayName - ユーザアカウントに対する人間にとって読みやすい名称、表示のためだけに利用されることを想定。例えば「Alex P.
   Müller」、「田中
   倫」。ServerPublicKeyCredentialCreationOptionsRequest.displayNameに対応する。
 * WebAuthn仕様書に記述されているPublicKeyCredentialEntityを拡張している

7.3.4.3 SERVERPUBLICKEYCREDENTIALDESCRIPTION

一般的にWebAuthnに記述されているPublicKeyCredentialDescriptorと同等だが、idのBufferSourceはbase64urlエンコーディングが代わりに利用される。

    dictionary ServerPublicKeyCredentialDescriptor {
        required PublicKeyCredentialType      type;
        required DOMString                    id;
        sequence<AuthenticatorTransport>      transports;
    };


 * 必須 type - WebAuthn仕様書に記述されたPublicKeyCredentialTypeとして定義されたディクショナリ
 * 必須 id - 呼び出し関数に参照される公開鍵クレデンシャルのクレデンシャルIDのbase64urlエンコードされた値
 * transports - WebAuthn仕様書に記述されたAuthenticatorTransportの配列


7.4 認証

このセクションはこの認証でサーバとやり取りするメッセージの概要から始め、続いてこれらのメッセージ例について示し、メッセージの特定のIDL定義を示す。「認証」とはWebAuthnで使われている用語集に従って「クレデンシャルの取得」、「クレデンシャルリクエスト」または「認証アサーションの取得」を指すことがあることに注意する。

7.4.1 認証の概要

登録について書かれた通信フローと同様に、認証フローはサーバと交換する4つのメッセージが必要である。メッセージの最初のペアは、クライアントからサーバへのServerPublicKeyCredentialGetOptionsRequestのフォーマットのリクエストと、サーバが対応した結果としてクライアントに返却するServerPublicKeyCredentialGetOptionsResponseである。このServerPublicKeyCredentialGetOptionsResponseはWebAuthnのnavigator.credentials.get()関数のパラメータとして利用されることを想定している。navigator.credentials.get()の結果はクライアントによってresponseフィールドにServerAuthenticatorAssertionResponseを設定したServerPublicKeyCredentialに整形され、サーバに送信する。サーバは[WebAuthn]仕様書のセクション7.2に従ってアサーションを検証し、結果に応じたServerResponseを返却する。

7.4.2 認証例

7.4.2.1 認証例:CREDENTIAL GET OPTIONS

リクエスト:

 * URL:/attestation/options
 * メソッド:POST
 * URLパラメータ:なし
 * ボティ:application/jsonエンコードされたServerPublicKeyCredentialGetOptionsRequest

    {
        "username": "johndoe@example.com",
        "userVerification": "required"
    }


成功レスポンス:

 * HTTPステータスコード:200 OK
 * ボティ:application/jsonエンコードされたServerPublicKeyCredentialGetOptionsResponse

{
    "status": "ok",
    "errorMessage": "",
    "challenge": "6283u0svT-YIF3pSolzkQHStwkJCaLKx",
    "timeout": 20000,
    "rpId": "https://example.com",
    "allowCredentials": [
        {
            "id": "m7xl_TkTcCe0WcXI2M-4ro9vJAuwcj4m",
            "type": "public-key"
        }
    ],
    "userVerification": "required"
}


エラーレスポンス:

 * HTTPステータスコード:4XXまたは5XX
 * ボディ:application/jsonエンコードされたServerResponse

    {
        "status": "failed",
        "errorMessage": "User does not exists!"
    }


呼び出しサンプル:

    fetch('/attestation/options', {
        method  : 'POST',
        credentials : 'same-origin',
        headers : {
            'Content-Type' : 'application/json'
        },
        body: JSON.stringify({
            "username": "johndoe@example.com",
            "userVerification": "required"
        })
    }).then(function (response) {
        return response.json();
    }).then(function (json) {
        console.log(json);
    }).catch(function (err) {
        console.log({ 'status': 'failed', 'error': err });
    })


7.4.2.2 認証例:AUTHENTICATOR ASSERTION RESPONSE

リクエスト:

 * URL:/assertion/result
 * メソッド:POST
 * URLパラメータ:なし
 * ボディ:responseフィールドにServerAuthenticatorAssertionResponseを設定されているapplication/jsonエンコードされたServerPublicKeyCredential

    {
        "id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
        "rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
        "response":{
            "authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAAA",
            "signature":"MEYCIQCv7EqsBRtf2E4o_BjzZfBwNpP8fLjd5y6TUOLWt5l9DQIhANiYig9newAJZYTzG1i5lwP-YQk9uXFnnDaHnr2yCKXL",
            "userHandle":"",
            "clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ4ZGowQ0JmWDY5MnFzQVRweTBrTmM4NTMzSmR2ZExVcHFZUDh3RFRYX1pFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9"
        },
        "type":"public-key"
    }


成功レスポンス:

 * HTTPステータスコード:200 OK
 * ボディ:application/jsonエンコードされたServerResponse

{
    "status": "ok",
    "errorMessage": ""
}


エラーレスポンス:

 * HTTPステータスコード:4XXまたは5XX
 * ボディ:application/jsonエンコードされたServerResponse

    {
        "status": "failed",
        "errorMessage": "Can not validate response signature!"
    }


呼び出しサンプル:

    fetch('/assertion/result', {
        method  : 'POST',
        credentials : 'same-origin',
        headers : {
            'Content-Type' : 'application/json'
        },
        body: JSON.stringify({
            "id":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
            "rawId":"LFdoCFJTyB82ZzSJUHc-c72yraRc_1mPvGX8ToE8su39xX26Jcqd31LUkKOS36FIAWgWl6itMKqmDvruha6ywA",
            "response":{
                "authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAAA",
                "signature":"MEYCIQCv7EqsBRtf2E4o_BjzZfBwNpP8fLjd5y6TUOLWt5l9DQIhANiYig9newAJZYTzG1i5lwP-YQk9uXFnnDaHnr2yCKXL",
                "userHandle":"",
                "clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ4ZGowQ0JmWDY5MnFzQVRweTBrTmM4NTMzSmR2ZExVcHFZUDh3RFRYX1pFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9"
            },
            "type":"public-key"
        })
    }).then(function (response) {
        return response.json();
    }).then(function (json) {
        console.log(json);
    }).catch(function (err) {
        console.log({ 'status': 'failed', 'error': err });
    })


7.4.3 AUTHENTICATOR IDL

7.4.3.1 SERVERPUBLICKEYCREDENTIALGETOPTIONSREQUEST

    dictionary ServerPublicKeyCredentialGetOptionsRequest {
        required DOMString              username;
        UserVerificationRequirement     userVerification = "preferred";
    };


 * 必須 username -
   エンティティについての人間が読むことができる名称。例えば「alexm」、「alex.p.mueller@example.com」や「+14255551234」
 * userVerfication -
   「required」、「preferred」、「discouraged」を設定する。詳細はWebAuthn仕様書を参照する。デフォルトは「preferred」。

7.4.3.2 SERVERPUBLICKEYCREDENTIALGETOPTIONSRESPONSE

    dictionary ServerPublicKeyCredentialGetOptionsResponse : ServerResponse {
        required DOMString                   challenge;
        unsigned long                        timeout;
        USVString                            rpId;
        sequence<ServerPublicKeyCredentialDescriptor> allowCredentials = [];
        UserVerificationRequirement          userVerification = "preferred";
        AuthenticationExtensionsClientInputs extensions;
    };


 * 必須 challenge - base64urlエンコードされたランダムなチャレンジ、チャレンジは最小でも16バイト長とし、最大は64バイト長とする
 * timeout - タイムアウト(ms)
 * rpId -
   このオプショナルな属性は呼び出し元が要求するリライングパーティの識別子を設定する。省略された場合、その値はCredentialsContainerオブジェクトに関連した設定オブジェクトののオリジンの有効ドメインになる
 * excludeCredentials - 本ドキュメントに記述されたServerPublicKeyCredentialDescriptorの配列
 * userVerification -
   「required」、「preferred」、「discouraged」を設定する。詳細はWebAuthn仕様書を参照する。デフォルトは「preferred」。ServerPublicKeyCredentialGetOptionsRequest.userVerificationに対応した値。
 * extensions -
   WebAuthn仕様書に記述されているAuthenticationExtensionsClientInputsが設定されたディクショナリ
 * 本ドキュメントに記述されたServerResponseを拡張している

7.4.3.3 SERVERPUBLICKEYCREDENTIALDESCRIPTOR

dictionary ServerAuthenticatorAssertionResponse : ServerAuthenticatorResponse {
    required DOMString      clientDataJSON;
    required DOMString      authenticatorData;
    required DOMString      signature;
    required DOMString      userHandle;
};


 * 必須 clientDataJSON - base64urlエンコードされたclientDataJSONバッファ
 * 必須 authenticatorData- base64urlエンコードされたauthenticatorDataバッファ
 * 必須 signature- base64urlエンコードされたsignatureバッファ
 * 必須 userHandle-
   base64urlエンコードされたuserHandleバッファ。登録済ユーザのServerPublicKeyCredentialUserEntity.idに対応している。


7.5 共通

7.5.1 COMMON IDL

7.5.1.1 SERVERRESPONSE

    dictionary ServerResponse {
        required Status     status;
        required DOMString  errorMessage = "";
    }


 * 必須 status - レスポンスのステータスを示す。「ok」または「failed」のいずれかを設定できる。
 * 必須 errorMessage - statusが「failed」だった場合、このフィールは空にしてはならない(MUST NOT)。

—

リファレンスなどは省略した。

WebAuthnと競合する部分は少なかったと思う。バイナリ型の部分はbase64urlエンコードして送信することくらい。 追加して定義している部分としては、

 * レスポンスの基本的な形をServerResponseで定めていること、
 * サーバが実装すべき暗号アルゴリズムが指定されていること、
 * 登録・認証とどちらでもクライアントからサーバに送るリクエストが定められていること、
 * 登録でのクライアントからのリクエストに含まれるattestationフィールドに応じてサーバが値を返却すること、
 * 認証でのクライアントからのリクエストに含まれるuserVerificationフィールドに応じてサーバが値を返却すること

のはず。 この仕様書ではレスポンスの形がWebAuthnよりも定義されているので、適合性テストのためには対応しておく必要がある(【 FIDO
Conformance Toolsメモ 】イントロダクション - 生き恥)。

FIDO FIDO2

s1r-J 140日前




広告を非表示にする

 * もっと読む

コメントを書く
2021-12-08


WINDOWS HELLOのPIN入力によるFIDO2認証はEDGEのINPRIVATEブラウザ(シークレットモード)では使えないかもしれない

FIDO 認証・認可

Windows HelloのPIN入力によるFIDO2認証はEdgeのInPrivateブラウザ(シークレットモード)では使えないかもしれない。
「かもしれない」というあやふやな書き方をしたのは、現状Microsoftなどの公式情報を探せていないから。また、顔認証や指紋認証については設定していないので不明である。

Edgeの検証結果と他のブラウザの対応状況も確認したのでメモしておく。 検証ではWebAuthn.ioというサイトを利用した。

事前にWindows Helloの設定をしておく必要があるが、ここでは説明しない。 下記にMS公式情報を載せておくので、実施するならば参考に設定してほしい。

Windows Hello の概要とセットアップ


検証


環境

 * OS: Windows10 バージョン20H2
 * Edge: 96.0.1054.43
 * GoogleChrome: 96.0.4664.45
 * FireFox: 94.0.2


EDGEの通常モード

最初にEdgeの通常モードでWindows Helloが利用できる様子をスクショで紹介しておく。

 1. WebAuthn.ioをEdgeで開く。



 1. ユーザ名をテキトーに入力し、Registerボタンを押す。



 1. Window HelloからPIN入力を求めるポップアップが表示される。PINを入力するとポップアップが閉じる。



 1. 少し待つと登録完了を伝える吹き出しが表示される。Loginボタンを押せば、ログインのために認証を求めてくる(未紹介)。



Edgeの通常モードではWindows Helloが呼び出され、PINを入力することでユーザ登録とログインができる。


EDGEのINPRIVATEブラウザ(シークレットモード)

 1. WebAuthn.ioをEdgeのInPrivateブラウザで開く。



 1. ユーザ名をテキトーに入力し、Registerボタンを押す。



 1. Window Helloからセキュリティキーを求めるポップアップが表示される。PIN入力を求める画面は表示されない。



上述のように、EdgeのInPrivateブラウザではWindows Helloは呼び出されるもののセキュリティキーの接続を求める画面が表示される。
ちなみにこのセキュリティキー画面は、Edge通常モードでPIN入力画面でキャンセルボタンを押すことでも表示される。


CHROMEのシークレットモード

スクショを撮っていないが、GoogleChromeのシークレットモードでも試した。
結果としては、Edgeと同様だった。つまり、通常モードでは最初にPIN入力を求められ、シークレットモードではセキュリティキーの接続を求める画面が表示された。


FIREFOXのプライベートモード

FirefoxはEdge、Chromeとは異なる挙動をし、プラベートモードでもPIN入力を求める画面が表示された。

 1. WebAuthn.ioをFirefoxのプライベートモードで開く。



 1. ユーザ名をテキトーに入力し、Registerボタンを押す。



 1. Window HelloからPIN入力を求めるポップアップが表示される。PINを入力するとポップアップが閉じる。



 1. 少し待つと登録完了を伝える吹き出しが表示される。。




結果

今回の検証結果をまとめておく(まとめる意味がある表だろうか??)。

ブラウザ モード Windows Hello PIN入力 Microsoft Edge 通常 ○ Microsoft Edge シークレット X Google
Chrome 通常 ○ Google Chrome シークレット X Mozilla Firefox 通常 ○ Mozilla Firefox シークレット ○
Vivaldi 通常 ○ Vivaldi シークレット X

検証までで紹介したブラウザ以外にVivaldiを使ってみたので、追記しておく。おそらくWindowsでは上3つのブラウザが主要なはずだ。今回の検証はデスクトップPCで実行したが、例えばサーフェスになったら挙動が異なるようなことはあるかもしれない。


考察(らしきもの)


PIN入力が利用できない理由

今回の一部ブラウザのシークレットモードでの動作だが、Windows
Hello自体はブラウザから呼び出されて利用できている。セキュリティキー(=認証器)であればそのままユーザ登録・ログイン認証ができるようなので、PIN入力だけが利用できない。どういった理由だろうか。

シークレットモードでは、セキュリティを高めるためにPIN入力という記憶要素だけではなく所持という要素が加わることでセキュリティが高められそうなセキュリティキーが必要なのだろうか。その場合、指紋や顔認証の扱いが気になる。所持要素を重視しているのであれば、シークレットモードでは指紋や顔認証もPIN同様に使えないことになる。

もう一つとしては、シークレットモードの目的である。Edgeのシークレットモードの説明では、

> このデバイスを使用している他のユーザーは閲覧アクティビティを見ることができません

さらにChromeでも

> あなたのアクティビティは、このデバイスを利用する他のユーザーには表示されません。

と説明されている。シークレットモードのブラウザを使う理由はデバイスを共有する他のユーザから情報を秘匿するためにある。ということは、デバイスにクレデンシャル情報が残ってしまうWindows
Hello PIN入力はこの場合に不適切なのかもしれない。また、Windows Hello
PIN入力はユーザサインインにも利用でき、わざわざシークレットモードで他のユーザから隠しているのにサインインしているユーザが知っている情報による認証は不適切ということかもしれない。この場合、顔認証や指紋認証も利用できないはずだ。


同じ動作するブラウザ、異なる動作をするブラウザ

EdgeとChromeが同じ動作をしてFirefoxが異なる動作という点も気になる。なにかMicrosoftがWindows
Helloに関する仕様を提示していて、Chromeはそれに従ったがFirefoxは従っていないのだろうか。
もしくは、EdgeとChromeのベースになっているChromiumにシークレットモードではPINを使えなくする実装が入っているのかもしれない。VivaldiはChromiumをベースとしていること、Chromium開発にはMicrosoftも関わっていることから可能性はある。

–

今回のシークレットモードブラウザでのWindows Helloについてきちんとした情報ソースは得られていない。
後日わかったことがあれば、追記したいと思っているし、有識者からの情報提供もお願いしたい。

FIDO Windows Hello ブラウザ

s1r-J 160日前




広告を非表示にする

 * もっと読む

コメントを書く
次のページ

プロフィール
s1r-J
読者です 読者をやめる 読者になる 読者になる

このブログについて
最新記事
 * sinonのuseFakeTimersはsetTimeoutと一緒に使用できない
 * Web Authentication API 9章 WebAuthn拡張機能の日本語訳
 * mkcertを使ってlocalhostをHTTPS化して開発する
 * AWS EC2でcurl: (60) SSL certificate problem: certificate has expired
 * Node.jsで証明書チェーンの検証をおこなう(jsrsasign編)

検索

カテゴリー
 * AWS (2)
 * FIDO (5)
 * Java (1)
 * JavaScript (3)
 * お知らせ (1)
 * その他IT (1)
 * 便利 (1)
 * 日本語訳 (4)
 * 環境構築 (7)
 * 設定 (1)
 * 認証・認可 (7)

月別アーカイブ
 * ▼ ▶
   2022 (5)
   * 2022 / 4 (2)
   * 2022 / 3 (1)
   * 2022 / 2 (1)
   * 2022 / 1 (1)
 * ▼ ▶
   2021 (19)
   * 2021 / 12 (2)
   * 2021 / 11 (2)
   * 2021 / 9 (1)
   * 2021 / 8 (1)
   * 2021 / 7 (1)
   * 2021 / 6 (1)
   * 2021 / 5 (2)
   * 2021 / 4 (2)
   * 2021 / 3 (1)
   * 2021 / 2 (3)
   * 2021 / 1 (3)


s1r-Jの技術ブログ

Powered by Hatena Blog | ブログを報告する



引用をストックしました

ストック一覧を見る 閉じる

引用するにはまずログインしてください

ログイン 閉じる

引用をストックできませんでした。再度お試しください

閉じる

限定公開記事のため引用できません。

読者です 読者をやめる 読者になる 読者になる