如何解决Xamarin iOS SecKeyChain InteractionNotAllowed问题? [英] How to resolve Xamarin iOS SecKeyChain InteractionNotAllowed issue?

查看:128
本文介绍了如何解决Xamarin iOS SecKeyChain InteractionNotAllowed问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的Xamarin.iOS项目中,我使用SecRecord/SecKeyChain来存储令牌值和应用程序版本.从生产日志中,当尝试在钥匙串中写入/读取项目时,我发现了状态码为'InteractionNotAllowed'的钥匙串相关异常. Apple文档指出,要解决InteractionNotAllowed错误,我们需要将默认的kSecAttrAccessible属性值从" WhenUnlocked "更改为"始终". 但是在我现有的代码中,当我将可访问性属性更改为"始终"应用时,由于该应用无法从钥匙串读取令牌而注销.读取时返回"未找到项目".但是,当我尝试再次保存令牌时,它会返回"重复项".因此,我再次尝试删除相同的项目,但是这次它再次返回"未找到项目".真是奇怪,我无法删除它,也无法使用相同的键来读取它.

In my Xamarin.iOS project I used SecRecord/SecKeyChain to store my token values and app version. From production log I found keychain related exceptions with status code 'InteractionNotAllowed' when try to write/read items in keychain. Apple documents states that to resolve InteractionNotAllowed error we need to change the default kSecAttrAccessible attribute value from ‘WhenUnlocked' to ‘Always’. But in my existing code when I changed accessible attribute to 'Always' app log out because it failed to read token from keychain. It return’s 'Item not found' when read. But when I tried to save token again it returns 'Duplicate item'. So again I tried to remove same item but this time it again returns 'Item not found'. That’s really strange I can’t delete it and I can’t read it with same 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;
        }

任何帮助都会很棒!谢谢

Any help would be great! Thanks

推荐答案

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

The property Service is also an unique identification when we store or retrieve data using KeyChain. You didn't post your GetValueFromAccountAndKey() method, so we don't know what is the key used for? But in your case, you should use the same Service to retrieve value:

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设置为App.AppName >.

Since you just make a hard code in your CreateRecordForNewKeyValue()( the Service has been written as a constant ), if you want to retrieve your value you should also set the Service as App.AppName in the method GetValueFromAccountAndKey().

读取时返回找不到项目".但是当我尝试保存令牌时 再次返回重复项".

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都必须是唯一的.

This is because when we use the same Account but different Service to retrieve data, KeyChain can't find the corresponding SecRecord. This made you thought the SecRecord didn't exist, then use the same Account to store value. The Duplicate item result throws out. For a SecRecord, the Account and Service must both be unique.

这篇关于如何解决Xamarin iOS SecKeyChain InteractionNotAllowed问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆