#はじめに
WindowsデスクトップアプリでもWebAuthnっぽいことができるライブラリを作成したので紹介します。
- 2018/12/18 FEITIAN BioPass FIDO2での動作確認をしました
- 2018/12/24 Verify機能を追加しました
- 2019/01/14 NFC(Yubikey5)に対応しました
- 2019/03/09 ver 2.0.0.0
####注意事項
- 本ライブラリ・デモプログラムはWebAuthn、CTAPを勉強しながら作成した検証プログラムです。
- サンプルレベルの品質なので重要なクレデンシャル情報が入っている認証器は使わないことをお勧めします。
- 本ライブラリ・デモプログラムを利用することによって生じるいかなる問題についても、その責任を負いません。
#1.実行環境
- Windows10 1809
- .Net Framework 4.6.1
- 『2』って書いてある青いYubikey
- FEITIAN BioPass FIDO2
- YubiKey5
- NFCリーダー Sony PaSoRi RC-S380
※Webとか名前がついていますが、サーバーと通信するなど一切ありません。
#2.とりあえず動かしてみる
- お手元に『2』って書いてある青いYubikeyをご用意ください
- あるいはFEITIAN BioPass FIDO2でもOK
- あるいはYubikey5でもOK
- ココからバイナリ一式をダウンロードします。
-
testUI02.exe
を実行します。 - YubikeyをUSBに刺して「get info」をクリックして「Success」って出たらOKです。
- あるいは パソリにYubikey5を乗っけて「get info」をクリックして「Success」って出たらOKです。
##get Info
Authenticatorの情報をGETします。GETして表示するだけです。
##Register
登録。WebAuthnのcredentials.create()
ぽいことをやります。
UserNameを入力してNextして登録します。
YubikeyにPINが設定してある場合は CTAP2_ERR_PIN_REQUIRED PIN
とエラーになるので画面右上PIN欄にPINを入力してからNextボタンをクリックしてください。
Enable username-less loginにチェックをつけるとResident Key
します。
BioPassで指紋認証したい場合は、User Verification
にチェックONしてください。
※Regident Key
は認証器にユーザー情報を保存する機能ですが、無限に保存できるわけではありません、認証器の保存領域MAXになった時の動作はYubikeyの場合、変なエラーが起きるようになるなど、あんまりうれしくない挙動になるようですのでご注意ください。
※2019/2/11追記 青いYubikeyでは26個目の登録(authenticatorMakeCredential)で 0x28:CTAP2_ERR_KEY_STORE_FULL
のエラーになりました。
##Login
認証。WebAuthnのcredentials.get()
ぽいことをやります。
Credential Idを指定してget()します。
このサンプルは直前にRegisterしたCredential Idをメモリに保持して再利用するだけです。
なので、Registerの後にやってください。
※BioPassで指紋認証したい場合は、User Verification
にチェックONしてください。
##Login without username
こちらも認証ですが、Credential Idを指定しない方法です。
なのでRegisterしないでも実行可能です。
#3.ライブラリ解説
ソースは以下の場所です。
https://github.com/gebogebogebo/WebAuthnModokiDesktop
##ライブラリ構成
ライブラリは64bit専用です。
- WebAuthnModokiDesktop.dll(本体)
- BouncyCastle.dll(Bouncy Castle)
- Newtonsoft.Json.dll(Newtonsoft.Json)
- CBOR(PeterO.Cbor)
- Numbers(PeterO.Numbers)
- HidLibrary(hidlibrary)
##プロジェクトへの組み込み
- Visual Studioでプロジェクトを作成します。
- ここからとってきたバイナリ一式(binフォルダの中身)をexeと同じフォルダに置いてください。
- Visual Studioの参照から
WebAuthnModokiDesktop.dll
を参照に追加してください。
##メソッド
using gebo.CTAP2.WebAuthnModokiDesktop;
using gebo.CTAP2;
- Credentials.Create()
- Credentials.Get()
- Credentials.Info()
- Credentials.SetPin()
- Credentials.ChangePin()
- Credentials.HidCheck()
- Credentials.NfcCheck()
- Credentials.SerializeAttestationToFile()
- Credentials.DeSerializeAttestationFromFile()
###Credentials.Create()
####引数
- DevParam devParam : デバイスの検索パラメータ※
- string publickeyJson : WebAuthnで指定するOptionをJSON文字列形式で指定します。
- string pin="" : PIN
※devParamについて
Authenticatorデバイスを検索するパラメータです。
とりあえず、gebo.CTAP2.DevParam.getDefaultParams()
をしておけばYubikeyとFEITIANのパラメータ設定されます。それ以外の製品は手動で追加してください。
####戻り値
- CreateCommandStatusクラス : 中にAttestationが入っています。
var devParam = DevParam.GetDefaultParams();
byte[] challenge = System.Text.Encoding.ASCII.GetBytes("this is challenge");
string pin = "xxxx";
string json =
"{" +
"rp : {" +
"id : 'demo.WebauthnMODOKI.gebogebo.com'," +
"}," +
"user : {" +
"id : 'userid'," +
"name :'name_name'," +
"displayName :'my name is gebogebo'," +
"}," +
"pubKeyCredParams: [{type: 'public-key',alg: -7}]," +
"timeout: 60000," +
"authenticatorSelection : {" +
"requireResidentKey : false," +
"}," +
string.Format($"challenge:[{string.Join(",", challenge)}],") +
"}";
var response = await Credentials.Create(devParam,json, pin);
if (response.isSuccess == true) {
// 成功
// response.attestation ...
}
##Credentials.Get()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string publickeyJson : WebAuthnで指定するOptionをJSON文字列形式で指定します。
- string pin="" : PIN
####戻り値
- getcommandstatusクラス : 中にAssertionが入っています。
- Assertionは複数取れる場合があるのでリストです。
var devParam = DevParam.GetDefaultParams();
byte[] challenge = System.Text.Encoding.ASCII.GetBytes("this is challenge");
string pin = "xxxx";
byte[] CredentialId= // createでGETしたCredentialIdを指定してください(response.attestation.CredentialId);
string json =
"{" +
"timeout : 60000," +
string.Format($"challenge:[{string.Join(",", challenge)}],") +
"rpId : 'demo.WebauthnMODOKI.gebogebo.com'," +
"allowCredentials : [{" +
string.Format($"id : [{string.Join(",", CredentialId)}],") +
"type : 'public-key'," +
"}]," +
"requireUserPresence : 'true'," +
"userVerification : 'discouraged'," +
"}";
var response = await Credentials.Get(devParam ,json, pin);
if (response.isSuccess == true) {
// 成功
// response.assertions[0] ...
}
##Credentials.SetPin()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string newpin : 設定するPIN
####戻り値
- commandstatusクラス : 処理結果です
PINを初めてセットするときに使うコマンドです。
PIN変更ではありません。
##Credentials.ChangePin()
####引数
- DevParam devParam : デバイスの検索パラメータ
- string newpin : 設定するPIN
- string currentpin : 現在のPIN
####戻り値
- commandstatusクラス : 処理結果です
##Credentials.Info()
####引数
- List hidParams : HIDデバイスの検索パラメータ
####戻り値
- infocommandstatusクラス : Authenticatorの情報
##Credentials.HidCheck()
####引数
- List hidParams
USBにささっているAuthenticatorの情報を取ってきます。
##Credentials.NfcCheck()
####引数
- List nfcParams
NFCリーダーにのっかっているAuthenticatorの情報を取ってきます。
##Credentials.SerializeAttestationToFile()
####引数
- CTAPResponseAttestation att : attestationクラス
- string pathname: シリアライズするファイルのパスとファイル名
####戻り値
- bool : true(成功)/false(失敗)
attestationをファイルに保存します。
##Credentials.DeSerializeAttestationFromFile()
####引数
- string pathname: デシリアライズするファイルのパスとファイル名
####戻り値
- CTAPResponseAttestation : Attestationクラス
attestationをファイルからクラスにデシリアライズします。
#おつかれさまでした
- 対応していない・未検証の部分
応答値を検証する機能はありません。PINを変更する機能はありません。timeoutパラメータには対応していません。32bit環境では動作確認していません、多分動かないと思います。2って書いてある青いYubikey以外では動作確認していません。
- 夜 酒飲みながら書いているコードなので、怪しいところが多々あり。
- 参考にさせていただきました。