2015/06/07

WinRTでの資格情報の保存

WinRTで資格情報、パスワードであったりアクセストークンであったり、に限らず、秘密を要する情報を安全に保存するにはWindows.Security.Credentialsが使えます。
ざっくりまとめると、
  • リソース名(Resource)、ユーザー名(UserName)、パスワード(Password)から成るPasswordCredentialオブジェクトをPasswordVaultクラスのメソッドを使って書き込み・読み出しする。
  • リソース名は、Twitterのようなサービス名、あるいは複数のユーザー情報をまとめるためのグループ名のようなもの。とくに分類する必要がなければ何でもいい。
  • ユーザー名とパスワードは、そのままの意味で使ってもいいし、要はDictionaryにおけるKeyとValueの組み合わせと同じなので、別の意味を持たせてもいい。
  • 一つのアプリで使えるPasswordCredentialの上限は10(後述)。
これをどう使うかというと、
  • 保存するときは、リソース名、ユーザー名、パスワードでPasswordCredentialを作成して、PasswordVault.Addメソッドを使う。

  • 保存したPasswordCredentialを取得するときは、
    • リソース名とユーザー名が分かっていればPasswordVault.Retrieveメソッドを使う。指定したPasswordCredentialが存在しなければ例外(System.Exception)が飛ぶので、要try-catch。
    • リソース名でまとめて取得するにはPasswordVault.FindAllByResourceメソッド、ユーザー名でまとめて取得するにはPasswordVault.FindAllByUserNameメソッドを使う。これらも存在しなければ例外が飛ぶので、要try-catch。
    • 全部まとめて取得するにはPasswordVault.RetrieveAllメソッドを使う。これは存在しなくても例外は飛ばないので、実はこれからフィルターして探し出せばtry-catchなしで済む。
    • なお、まとめて取得したPasswordCredentialはパスワードが空なので、パスワードを見るにはその前にPasswordCredential.RetrievePasswordメソッドを実行する要あり。

  • 保存したPasswordCredentialのパスワードを修正するときは、同じリソース名、ユーザー名でPasswordCredentialを作成してPasswordVault.Addメソッドを使えば上書きされる(PasswordCredentialを取得して修正しても、保存したものには反映されない)。

  • 保存したPasswordCredentialを削除するときは、一旦取得してからPasswordVault.Removeメソッドを使う。
これらをまとめてみたもの。
using System;
using System.Linq;
using System.Collections.Generic;
using Windows.Security.Credentials;
public class CredentialService
{
private string _resourceName;
private PasswordVault _vault;
public CredentialService(string resourceName)
{
if (String.IsNullOrEmpty(resourceName))
throw new ArgumentNullException("resourceName");
_resourceName = resourceName;
_vault = new PasswordVault();
}
public string Get(string key)
{
try
{
var credential = _vault.Retrieve(_resourceName, key);
return credential.Password;
}
catch
{
// If no such credential, a System.Exception will be thrown.
return null;
}
}
public void Set(string key, string value)
{
if (value != null)
{
// Create or overwrite a credential.
var credential = new PasswordCredential(_resourceName, key, value);
_vault.Add(credential);
}
else
{
// Remove a credential.
try
{
var credential = _vault.Retrieve(_resourceName, key);
_vault.Remove(credential);
}
catch
{
// If no such credential, a System.Exception will be thrown.
}
}
}
public IEnumerable<KeyValuePair<string, string>> GetAll()
{
var credentials = _vault.RetrieveAll().Where(x => x.Resource == _resourceName);
foreach (var credential in credentials)
{
credential.RetrievePassword(); // This is required to fill Password property.
yield return new KeyValuePair<string, string>(credential.UserName, credential.Password);
}
}
public void RemoveAll()
{
try
{
var credentials = _vault.FindAllByResource(_resourceName);
foreach (var credential in credentials)
{
_vault.Remove(credential);
}
}
catch
{
// If no such credential, a System.Exception will be thrown.
}
}
}
ここからGet/Setメソッドを静的メソッドにして、WinRTでの設定を保存するクラスでプロパティのバッキングストアに使えるようにすれば、秘密を要する情報も設定クラスでまとめて管理できるようになるかなと思います。

ちなみに、PasswordCredentialの上限はPasswordVault.Addメソッドの説明には10と書かれてますが、10より少ない状態から一気に作成すれば10を超えて作成できたりしたので、上限をチェックするアルゴリズムに現状は穴があるっぽいです。

それはともかく、量が多い場合は暗号化したファイルをLocalFolderなりRoamingFolderに保存しろという話で、そういう例もあります。

0 コメント :