Edited at

Touch ID: プライベートキーをTouch IDプロテクトする

More than 3 years have passed since last update.

アプリのマスターキーをJwkSetで管理したい


SecKind.GenericPassword で保存する


  • どうも SecKind.Key だと うまく行かない


const SecKind KIND = SecKind.GenericPassword;
public static readonly string SERVICE_NAME = "Auth.Touch";


マスターキーが存在したら削除する


  • KeyChain Itemのクエリレコード


static readonly SecRecord KEYCHAIN_QUERY = new SecRecord (KIND) {
Service = SERVICE_NAME,
UseOperationPrompt = "Authenticate yourself.",
};


  • 指定したKeyChain Itemを削除する


public override void Delete ()
{
Evaluate (SecKeyChain.Remove (KEYCHAIN_QUERY));
}


  • Success以外だと例外を出す


public static void Evaluate(SecStatusCode code){
if (code != SecStatusCode.Success) {
throw new KeyChainException (code);
}
}


JOSE JwkSetをシリアライズして入れてみる


  • JwkSet をシリアライズして、 SecRecordの Genericにぶち込む

  • コメントにあるようにTouch IDで守りたいのであれば、SecAccessControll を設定する

  • AccessControlを指定しないと、Touch IDのプロンプトでません


public static SecRecord ToKeyChainItem (
Jose.JwkSet jwkset,
SecAccessControl grant)
{
// if AccessControl is not specified,
// this security record is not protected by TouchID

return new SecRecord (KIND) {
Service = SERVICE_NAME,
Generic = NSData.FromString(jwkset.ToJson()),
UseNoAuthenticationUI = true,
AccessControl=grant,
};
}


  • Touch ID 用のAccess Controlは以下で渡す事にします


static readonly SecAccessControl GRANT = new SecAccessControl (
SecAccessible.WhenPasscodeSetThisDeviceOnly,
SecAccessControlCreateFlags.UserPresence);


  • 作成。Deleteの例外はItemNotFoundの場合、無視する。


public override Jose.JwkSet Create ()
{
try{
this.Delete ();
}catch(KeyChainException ex) {
if (ex.Code != SecStatusCode.ItemNotFound)
throw ex;
}
return this.Add (CreateJwkSet());
}


  • Add


public Jose.JwkSet Add(Jose.JwkSet jwkset)
{
var kci = ToKeyChainItem(jwkset, GRANT);
Evaluate (SecKeyChain.Add (kci));

return jwkset;
}


  • JwkSetはEC521で


public static Jose.JwkSet CreateJwkSet()
{
var jwk = Jose.Jwa.KeyDef.EC.GenerateKey (Jose.Jwa.Ec.CurveEnum.P_521);
jwk.kid = DateTime.Now.ToString (); // for TEST

return new Jose.JwkSet () {
keys = new System.Collections.Generic.List<Jose.Jwk>{ jwk }
};
}


参照する


  • Deleteを同じKeyChain Itemをレコードして探す

  • みつかったらJwkSetに復元する


public override Jose.JwkSet Load ()
{
SecStatusCode code;

SecRecord resultData = SecKeyChain.QueryAsRecord (
KEYCHAIN_QUERY, out code);
Evaluate(code);
return ToJwkSet(resultData.Generic);
}


動かす


  • 起動

image


  • 空で削除すると例外

image


  • 新規作成  & 保存

image


  • Browse すると Touch ID 認証

image


  • 認証が通ると表示

image


  • ちなみに“Enter Passcord”でパスコード認証も通ります

image


SecKind.Keyだとうまく行かない


  • Touch ID を使わないと以下のコードで SecKind.Keyで保存できるが、 Touch IDプロテクトすると Param エラー

SecStatusCode code = SecKeyChain.Add ( new SecRecord ( SecKind.Key ) {
Service = _service_name,
Label = _service_name,
Account = _account_name,
Generic = NSData.FromString ( jwkset.ToJson(), NSStringEncoding.UTF8 ),
Accessible = accessible,
Synchronizable = sync
} );


  • どのParamでエラーなのかがよくわからん