Skip to content

MS_WindowsSecurityCredentials

nishi_74322014 edited this page Sep 1, 2026 · 1 revision

Windows.Security.Credentials

概要

  • Microsoft Passport が提供するプログラミングシステムの API
  • Windows デバイス上で、FIDO を実装する場合に、この API を利用する。

補足(現在は WebAuthn が標準): 本ページの KeyCredentialManager
UWP アプリ向けの API で、ブラウザからは使えない。
Web アプリケーションから Windows Hello を認証器として使う場合は、
現在は W3C の **Web Authentication API(WebAuthn)**を用いる。
「鍵ペアを作り、公開鍵をサーバに登録し、チャレンジに署名する」という
流れは同じなので、本ページの構成はそのまま WebAuthn の理解にも使える。
詳しくは「Web Authentication API
WebAuthnを実装する。」を参照。

クライアント実装

共通

  • アプリケーションに登録開始リクエストを送る。
  • challenge、policy、appID などが返される。

policyに適合する認証器を調べる

認証器の調査: KeyCredentialManager.IsSupportedAsync()メソッド

  • ユーザーが Windows Hello をセットアップしたかどうかを調べる
if (await KeyCredentialManager.IsSupportedAsync() == false)
{
  ・・・

キーのストア&コンテナを開く: KeyCredentialManager.OpenAsync()メソッド

当該ユーザ&アプリケーションのキーのストア&コンテナを開く。

// retrieve private key for sign
KeyCredentialRetrievalResult res =
  await KeyCredentialManager.OpenAsync("keyName");

登録

if (res.Status == KeyCredentialStatus.NotFound)
{
  ・・・

の場合、

  • ≒ 新規の device を登録する場合、若しくは、追加のデバイスを登録する場合。

キーペアを作成する: KeyCredentialManager.RequestCreateAsync()メソッド

  • RequestCreateAsync を使用してキーペアを作成する。

    • Windows Hello に対応した認証器に生体認証の情報を入力する。
    • 入力後、内部では、private key と public key が作成される。
    • private key は TPM に登録され、以降、認証器によって保護される。
  • 実装

// Create the credential
// (Windows Hello is diplayed here !)
KeyCredentialRetrievalResult createRes =
    await KeyCredentialManager.RequestCreateAsync("keyName", KeyCredentialCreationOption.ReplaceExisting);

// if the status is success, retrieve the public key.
if (createRes.Status == KeyCredentialStatus.Success)
{
  ・・・public keyとattestationを取得する。
}
else if(
    keyCreationResult.Status == KeyCredentialStatus.UserCanceled ||
    keyCreationResult.Status == KeyCredentialStatus.UserPrefersPassword)
{
    // Show error message to the user to get confirmation that user does not want to enroll.
}

移行メモ(変数名の不一致): 上記のコードは、
作成結果を createRes に受けておきながら
分岐では keyCreationResult を参照している。
元ページのままとしたが、実装時はどちらかに統一すること
(以降のコードは keyCreationResult を前提にしている)。

public keyを取得する: KeyCredential.RetrievePublicKey()メソッド

  • public key を取得する。

  • 実装

KeyCredential userKey = keyCreationResult.Credential;
IBuffer publicKey = userKey.RetrievePublicKey();

attestationを取得する: KeyCredential.GetAttestationAsync()メソッド

  • attestation(デバイス正常性構成証明)を取得する。

  • 実装

KeyCredentialAttestationResult keyAttestationResult = await userKey.GetAttestationAsync();

IBuffer keyAttestation = null;
IBuffer certificateChain = null;
bool keyAttestationIncluded = false;
bool keyAttestationCanBeRetrievedLater = false;
KeyCredentialAttestationStatus keyAttestationRetryType = 0;

if (keyAttestationResult.Status == KeyCredentialAttestationStatus.Success)
{
    keyAttestationIncluded = true;
    keyAttestation = keyAttestationResult.AttestationBuffer;
    certificateChain = keyAttestationResult.CertificateChainBuffer;
    rootPage.NotifyUser("Successfully made key and attestation", NotifyType.StatusMessage);
}
else if (keyAttestationResult.Status == KeyCredentialAttestationStatus.TemporaryFailure)
{
    keyAttestationRetryType = KeyCredentialAttestationStatus.TemporaryFailure;
    keyAttestationCanBeRetrievedLater = true;
    rootPage.NotifyUser("Successfully made key but not attestation", NotifyType.StatusMessage);
}
else if (keyAttestationResult.Status == KeyCredentialAttestationStatus.NotSupported)
{
    keyAttestationRetryType = KeyCredentialAttestationStatus.NotSupported;
    keyAttestationCanBeRetrievedLater = false;
    rootPage.NotifyUser("Key created, but key attestation not supported", NotifyType.StatusMessage);
}

補足(attestation は「鍵の出自の証明」): attestation は
その鍵が本当に TPM の中で作られ、外に出ていないこと
デバイス側が証明するものである。
NotSupported(TPM を持たないソフトウェア鍵)や
TemporaryFailure(構成証明サービスに到達できない)でも
鍵自体は使えるため、サーバ側で
「attestation なしの登録を受け入れるか」を方針として決める必要がある。
上のコードが状態を細かく持ち回っているのはこのためである。

上記の情報をサーバに飛ばして登録する。

  • PublicKey

    IBuffer publicKey
  • KeyAttestation

    IBuffer keyAttestation
    • certificate chain for attestation endorsement key

      IBuffer certificateChain
    • status code of key attestation result

      bool keyAttestationIncluded
      bool keyAttestationCanBeRetrievedLater
      KeyCredentialAttestationStatus keyAttestationRetryType
  • 実装

// Package public key, keyAttesation if available,
// certificate chain for attestation endorsement key if available,
// status code of key attestation result: keyAttestationIncluded or
// keyAttestationCanBeRetrievedLater and keyAttestationRetryType
// and send it to application server to register the user.
bool serverAddedPassportToAccount = await AddPassportToAccountOnServer();

if (serverAddedPassportToAccount == true)
{
    return true;
}

認証

上記の KeyCredentialManager.OpenAsyncの結果が、

if (res.Status == KeyCredentialStatus.Success)
{
  ・・・

の場合、

KeyCredentialManager.RequestSignAsync()メソッド

登録後、PIN や 生体認証を使って、challenge にデジタル署名 (RequestSignAsync) する。

var openKeyResult = await KeyCredentialManager.OpenAsync(AccountId);
if (openKeyResult.Status == KeyCredentialStatus.Success)
{
    var userKey = openKeyResult.Credential;
    var publicKey = userKey.RetrievePublicKey();
    var signResult = await userKey.RequestSignAsync(message);
    if (signResult.Status == KeyCredentialStatus.Success)
    {
        return signResult.Result;
    }
    else if (signResult.Status == KeyCredentialStatus.UserPrefersPassword)
    {
    }
}

上記の情報をサーバに飛ばして認証する。

サーバ実装の「認証」を参照。

その他

UserConsentVerifier.RequestVerificationAsync()メソッド

ある特定の箇所で生体認証を呼び出して OK か NO か確認したい場合

if (await UserConsentVerifier.CheckAvailabilityAsync() ==
UserConsentVerifierAvailability.Available)
{
    UserConsentVerificationResult res =
        await UserConsentVerifier.RequestVerificationAsync(
            "This is sensitive operation ! Please authenticate again.");

    if(res == UserConsentVerificationResult.Verified)
    {
      // some important action …
    }
}

補足(署名を伴わない確認): UserConsentVerifier
その場で本人確認のダイアログを出すだけで、鍵による署名は行わない。
端末内での再確認(重要操作の前の確認)には使えるが、
サーバ側で検証可能な証跡は残らないため、
認証そのものには RequestSignAsync を使う。

KeyCredentialManager.DeleteAsync()メソッド

private key を削除する。

await KeyCredentialManager.DeleteAsync("keyName");

サーバ実装

サンプルのデータベース スキーマ

User

  • id
  • e-mail address
  • first name
  • last name

UserKeys

  • id
  • user id
  • public key
  • attestation
  • device id
  • last logontime

補足(1 ユーザに複数の鍵): 鍵は端末ごとに作られるため、
User : UserKeys は 1 対多になる。
端末を買い替えたら新しい鍵を追加登録し、
紛失した端末の鍵は削除する、という運用になる。
上のスキーマに device id があるのはこのためである。

処理

サーバ側の登録

UserKeys レコードを追加。

サーバ側の認証

using (RSACng pubKey = new RSACng(publicKey))
{
   retval = pubKey.VerifyData(
                originalChallenge, responseSignature,
                HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}

補足(challenge はサーバが作って覚えておく): 検証で使う
originalChallenge は、サーバが毎回生成してセッションに保持したものでなければ
リプレイ攻撃を防げない。クライアントから送り返された値をそのまま使ってはならない。

参考

docs.microsoft.com

Microsoft/Windows-universal-samples

本 Wiki 内


Tags: Windows, 認証基盤

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally