ios - 如何解决 Xamarin iOS SecKeyChain InteractionNotAllowed 问题?

标签 ios objective-c xamarin xamarin.forms xamarin.ios

在我的 Xamarin.iOS 项目中,我使用 SecRecord/SecKeyChain 来存储我的 token 值和应用程序版本。从生产日志中,当尝试写入/读取钥匙串(keychain)中的项目时,我发现状态代码为“InteractionNotAllowed”的钥匙串(keychain)相关异常。 Apple 文档指出,要解决 InteractionNotAllowed 错误,我们需要将默认的 kSecAttrAccessible 属性值从“WhenUnlocked”更改为“Always”。 但是在我现有的代码中,当我将可访问属性更改为“Always”时,应用程序注销,因为它无法从钥匙串(keychain)中读取 token 。读取时返回“未找到项目”。但是当我再次尝试保存 token 时,它返回“重复项”。所以我再次尝试删除相同的项目,但这次它再次返回“找不到项目”。这真的很奇怪,我不能删除它,我不能用相同的 key 读取它。

下面是代码片段-

private SecRecord CreateRecordForNewKeyValue(string accountName, string value)
        {
            return new SecRecord(SecKind.GenericPassword)
            {
                Service = App.AppName,
                Account = accountName,
                ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
                Accessible = SecAccessible.Always //This line of code is newly added.
            };
        }



private SecRecord ExistingRecordForKey(string accountName)
        {
            return new SecRecord(SecKind.GenericPassword)
            {
                Service = App.AppName,
                Account = accountName,
                Accessible = SecAccessible.Always //This line of code is newly added. 
            };
        }



public void SetValueForKeyAndAccount(string value, string accountName, string key)
        {
            var record = ExistingRecordForKey(accountName);
            try
            {
                if (string.IsNullOrEmpty(value))
                {
                    if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
                        RemoveRecord(record);
                    return;
                }
                // if the key already exists, remove it before set value
                if (!string.IsNullOrEmpty(GetValueFromAccountAndKey(accountName, key)))
                    RemoveRecord(record);
            }
            catch (Exception e)
            {
                //Log exception here -("RemoveRecord Failed " + accountName, e,);
            }
            //Adding new record values to keychain
            var result = SecKeyChain.Add(CreateRecordForNewKeyValue(accountName, value));
            if (result != SecStatusCode.Success)
            {
                if (result == SecStatusCode.DuplicateItem)
                {
                    try
                    {
                        //Log exception here -("Error adding record: {0} for Account-" + accountName, result), "Try Remove account");
                        RemoveRecord(record);
                    }
                    catch (Exception e)
                    {
                        //Log exception here -("RemoveRecord Failed  after getting error SecStatusCode.DuplicateItem for Account-" + accountName, e);
                    } 
                }
                else
                    throw new Exception(string.Format("Error adding record: {0} for Account-" + accountName, result));
            }
        }    


public string GetValueFromAccountAndKey(string accountName, string key)
        {
            try
            {
                var record = ExistingRecordForKey(accountName);
                SecStatusCode resultCode;
                var match = SecKeyChain.QueryAsRecord(record, out resultCode);
                if (resultCode == SecStatusCode.Success)
                {
                    if (match.ValueData != null)
                    {
                        string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
                        if (string.IsNullOrEmpty(valueData))
                            return string.Empty;
                        return valueData;
                    }
                    else if (match.Generic != null)
                    {
                        string valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
                        if (string.IsNullOrEmpty(valueData))
                            return string.Empty;
                        return valueData;
                    }
                    else
                        return string.Empty;
                }
            }
            catch (Exception e)
            {
                // Exception logged here -("iOS Keychain Error for account-" + accountName, e);
            }
            return string.Empty;
        }

任何帮助都会很棒!谢谢

最佳答案

属性Service也是我们使用KeyChain存储或检索数据时的唯一标识。您没有发布您的 GetValueFromAccountAndKey() 方法,所以我们不知道 key 的用途是什么?但在您的情况下,您应该使用相同的 Service 来检索值:

string GetValueFromAccountAndKey(string accoundName, string service)
{
    var securityRecord = new SecRecord(SecKind.GenericPassword)
    {
        Service = service,
        Account = accoundName
    };

    SecStatusCode status;
    NSData resultData = SecKeyChain.QueryAsData(securityRecord, false, out status);

    var result = resultData != null ? new NSString(resultData, NSStringEncoding.UTF8) : "Not found";

    return result;
}

因为你只是在你的 CreateRecordForNewKeyValue() 中做了一个硬编码(服务已经被写成常量),如果你想检索你的值你也应该设置 Service 作为方法 GetValueFromAccountAndKey() 中的 App.AppName

It return’s 'Item not found' when read. But when I tried to save token again it returns 'Duplicate item'.

这是因为当我们使用相同的Account但不同的Service获取数据时,KeyChain找不到对应的SecRecord。这让你以为 SecRecord 不存在,然后使用相同的 Account 来存储值。 Duplicate item 结果抛出。对于 SecRecordAccountService 都必须是唯一的。

关于ios - 如何解决 Xamarin iOS SecKeyChain InteractionNotAllowed 问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50348482/

相关文章:

ios - Swift GCD 中的持久线程

ios - 在调用 prepareForSegue 后有没有办法停止 segue

ios - 是否可以通过 iOS 中的应用内购买进行后付款?

php - 数学函数在 PHP 中运行良好,但在 Objective C 中运行错误

ios - 从文档目录中删除图像 ios Xamarin

ios - 带有谷歌地图的应用程序将在 ios6 中运行

iOS 应用程序首次启动加载条款和条件

ios - 如何在 Swift 上将 HTML 代码加载到 WebView

c# - Xamarin forms App() 在启动时被调用两次

xamarin - View 模型之间的 MvvmCross 通信