C#
iOS
Jose
TouchID

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でエラーなのかがよくわからん