Как решить проблему Xamarin iOS SecKeyChain InteractionNotAllowed? - PullRequest
0 голосов
/ 15 мая 2018

В моем проекте Xamarin.iOS я использовал SecRecord / SecKeyChain для хранения значений токенов и версии приложения.Из производственного журнала я обнаружил связанные с цепочкой для ключей исключения с кодом состояния «InteractionNotAllowed» при попытке записи / чтения элементов в цепочке для ключей.В документах Apple говорится, что для устранения ошибки InteractionNotAllowed нам нужно изменить значение атрибута kSecAttrAccessible по умолчанию с ' WhenUnlocked ' на ' Always '.Но в моем существующем коде, когда я изменил доступный атрибут на ' Always ', приложение выходит из системы, потому что оно не смогло прочитать токен из цепочки для ключей.Возвращает ' Элемент не найден ' при чтении.Но когда я снова попытался сохранить токен, он возвращает « Duplicate item ».Итак, я снова попытался удалить тот же элемент, но на этот раз он снова возвращает Элемент не найден .Это действительно странно, я не могу удалить это и не могу прочитать это с тем же ключом.

Ниже приведен фрагмент кода -

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;
        }

Любая помощь будет отличной!Спасибо

1 Ответ

0 голосов
/ 16 мая 2018

Свойство 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 как App.AppName в методе GetValueFromAccountAndKey().

. При прочтении возвращается «Предмет не найден».Но когда я снова пытаюсь сохранить токен, он возвращает «Duplicate item».

Это потому, что когда мы используем тот же Account, но другой Service для извлечения данных, KeyChain не может найтисоответствующий SecRecord.Это заставило вас подумать, что SecRecord не существует, а затем использовать тот же Account для хранения значения.Результат Duplicate item выбрасывает.Для SecRecord значения Account и Service должны быть уникальными.

...