Как я могу узнать, есть ли у пользователя iPhone в настоящий момент установлен код доступа и включено шифрование? - PullRequest
37 голосов
/ 27 сентября 2010

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

Однако я понял, что если пользователь не выбрал пароль и специально не включил защиту данных наэкран Настройки> Общие> Passcade Lock, тогда данные на самом деле не защищены вообще.

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

Ответы [ 6 ]

18 голосов
/ 31 мая 2011

Отказ от ответственности: Этот ответ был действителен до 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]];
13 голосов
/ 02 октября 2014

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

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

Поток:

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

Я проверил это на своем 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 голосов
/ 17 июля 2012

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

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

1 голос
/ 17 октября 2011

Независимо от 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;
    }

} 
0 голосов
/ 14 марта 2017

Свифт 3

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

Требуется iOS 9 или выше.

0 голосов
/ 14 марта 2017

Поскольку iOS 9 , существует флаг LAPolicyDeviceOwnerAuthentication в LocalAuthentication framework.

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

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

    if(passcodeEnabled) {
        return YES;
    }

    return NO;
}
...