Подтвердить что ты не робот

Как узнать, имеет ли пользователь iPhone в настоящее время набор паролей и шифрование?

Я пишу приложение iPhone, которое требует, чтобы его данные были зашифрованы. Я узнал, как включить шифрование файлов, установив атрибут NSFileProtectionComplete. Я также знаю, как проверить версию iPhone, чтобы убедиться, что они работают под управлением iOS 4.0 или выше.

Что я понял, что если пользователь не выбрал код доступа и специально не включил защиту данных на экране "Настройки" > "Основные" > "Паспортная блокировка", то данные фактически не защищены.

Я хочу выпустить предупреждение и сообщить пользователю, что он должен включить код доступа и включить защиту данных (для чего требуется резервное копирование и восстановление на до 4-х iPhone), а затем выйти из приложения, если они не будут имеют защиту паролем и данными. Я все равно не могу понять состояние этих настроек. Все API-интерфейсы, которые я нашел, такие как "protectedDataAvailable" в UIApplication, проходят успешно, если защита данных отключена.

4b9b3361

Ответ 1

Отказ от ответственности: этот ответ был действителен до тех пор, пока ios 4.3.3

Если защита данных включена, новый файл будет иметь nil NSFileProtectionKey по умолчанию.

Если защита данных отключена, новый файл будет иметь NSFileProtectionNone NSFileProtectionKey по умолчанию.

Таким образом, вы можете обнаружить наличие защиты файлов с помощью следующего кода:

NSString *tmpDirectoryPath = 
    [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
NSString *testFilePath = 
    [tmpDirectoryPath stringByAppendingPathComponent:@"testFile"];
[@"" writeToFile:testFilePath 
      atomically:YES
        encoding:NSUTF8StringEncoding
           error:NULL]; // obviously, do better error handling
NSDictionary *testFileAttributes = 
    [[NSFileManager defaultManager] attributesOfItemAtPath:testFile1Path
                                                     error:NULL];
BOOL fileProtectionEnabled = 
    [NSFileProtectionNone isEqualToString:[testFile1Attributes objectForKey:NSFileProtectionKey]];

Ответ 2

iOS 8 (OS X Yosemite) представил новый API/константу, используемый для определения, имеет ли пользовательское устройство код доступа.

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly может использоваться для определения того, установлен ли пароль на устройстве.

Поток:

  • Попытка сохранить новый элемент в цепочке ключей с указанным набором атрибутов
  • Если это удается, это означает, что в настоящий момент включен код доступа.
  • Если пароль не будет сохранен, это означает, что отсутствует код доступа
  • Очистите элемент, потому что, если он уже находится в цепочке ключей, он сделает ошибку "добавить", похоже, что код доступа не установлен.

Я тестировал это на своем iPhone 5S, сначала он вернул true, затем я отключил пароль в настройках, и он вернул false. Наконец, я снова включил код доступа, и он возвращает true. Предыдущие версии ОС вернут false. Код работает в симуляторе, возвращая true на машине с установленным паролем OS X (я не тестировал альтернативные сценарии OS X).

Также см. пример проекта здесь: https://github.com/project-imas/passcode-check/pull/5

Наконец, насколько мне известно, iOS 8 не имеет настройки для отключения защиты данных, поэтому я предполагаю, что это все, что вам нужно для обеспечения шифрования.

BOOL isAPIAvailable = (&kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly != NULL);

// Not available prior to iOS 8 - safe to return false rather than crashing
if(isAPIAvailable) {

    // From http://pastebin.com/T9YwEjnL
    NSData* secret = [@"Device has passcode set?" dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *attributes = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: @"LocalDeviceServices",
        (__bridge id)kSecAttrAccount: @"NoAccount",
        (__bridge id)kSecValueData: secret,
        (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
    };

    // Original code claimed to check if the item was already on the keychain
    // but in reality you can't add duplicates so this will fail with errSecDuplicateItem
    // if the item is already on the keychain (which could throw off our check if
    // kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly was not set)

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
    if (status == errSecSuccess) { // item added okay, passcode has been set
        NSDictionary *query = @{
            (__bridge id)kSecClass:  (__bridge id)kSecClassGenericPassword,
            (__bridge id)kSecAttrService: @"LocalDeviceServices",
            (__bridge id)kSecAttrAccount: @"NoAccount"
        };

        status = SecItemDelete((__bridge CFDictionaryRef)query);

        return true;
    }

    // errSecDecode seems to be the error thrown on a device with no passcode set
    if (status == errSecDecode) {
        return false;
    }
}

return false;

P.S. Поскольку Apple указывает на видео WWDC, представляющее это (711 Keychain и аутентификация с Touch ID), они решили не делать статус кода доступа напрямую доступным через API специально для того, чтобы предотвратить попадание приложений в ситуации, когда они не должны быть (т.е. "У этого устройства есть код доступа? Хорошо, отлично, я сохраню эту личную информацию в виде обычного текста". Было бы гораздо лучше создать ключ шифрования, сохранить его под kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly и зашифровать этот файл, который будет быть неустранимым, если пользователь решает отключить свой код доступа).

Ответ 3

Apple не предоставляет метод для определения того, установлен ли пользователь в наборе паролей.

Если вашему приложению требуется шифрование, вы должны подумать о шифровании и расшифровке файлов с помощью надежной реализации шифрования и запросить у пользователя пароль или сохранить ключ в цепочке ключей.

Ответ 4

Независимо от NSDataWritingAtomic или NSDataWritingFileProtectionComplete, результат для меня всегда один и тот же. Странное поведение, здесь код:

BOOL expandTilde = YES;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, expandTilde);
NSString *filePath;
filePath = [[paths lastObject] stringByAppendingPathComponent:@"passcode-check"];

NSMutableData *testData;
testData = [NSMutableData dataWithLength:1024];

NSLog(@"Attempt to write data of length %u file: %@", [testData length], filePath);

NSError *error = nil;

if (![testData writeToFile:filePath options:NSDataWritingAtomic error:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    return NO;
} else {
    NSLog(@"File write successful.");

    error = nil;
    NSDictionary *testFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];

    NSLog(@"Getting attributes: %@", testFileAttributes);

    if ([NSFileProtectionComplete isEqualToString:[testFileAttributes objectForKey:NSFileProtectionKey]]) {
        error = nil;
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
        // passcode disabled
        return YES;
    } else {
        error = nil;
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
        return NO;
    }

} 

Ответ 5

Поскольку iOS 9, в Локальная аутентификация существует флаг LAPolicyDeviceOwnerAuthentication.

+ (BOOL)isPasscodeEnabled
{
    NSError *error = nil;
    LAContext *context = [[LAContext alloc] init];

    BOOL passcodeEnabled = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error];

    if(passcodeEnabled) {
        return YES;
    }

    return NO;
}

Ответ 6

Swift 3

func isPasscodeEnabled() -> Bool {
    return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthentica‌​tion, error:nil)
}

требуется iOS 9 или более.