如何确定iPhone用户当前是否设置了密码并启用加密?

时间:2010-09-27 17:55:33

标签: iphone core-data encryption ios4 data-protection

我正在编写一个需要加密数据的iPhone应用程序。我已经学会了如何通过设置NSFileProtectionComplete属性来打开文件加密。我也知道如何检查iPhone版本以确保它们运行的​​是iOS 4.0或更高版本。

我已经意识到,如果用户没有选择密码并且没有在设置>上专门启用数据保护。一般> Passcade Lock屏幕然后数据实际上没有受到保护。

我想弹出一个警告并告诉用户他们必须启用密码并打开数据保护(这需要在4前iPhone上备份和恢复),然后退出应用程序,如果他们不启用密码和数据保护。我无论如何都无法弄清楚这些设置的状态。我发现的所有API,例如UIApplication中的“protectedDataAvailable”,如果禁用数据保护,都会成功通过。

6 个答案:

答案 0 :(得分:18)

免责声明:此答案有效,直到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]];

答案 1 :(得分:13)

iOS 8(OS X Yosemite)推出了一个新的API /常量,用于检测用户的设备是否有密码。

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly可用于检测设备上是否设置了密码。

流程是:

  1. 尝试使用该属性集
  2. 在钥匙串上保存新项目
  3. 如果成功则表示当前启用了密码
  4. 如果密码未保存,则表示没有密码
  5. 清理该项目,因为如果它已经在钥匙串上,它将使“添加”失败,看起来没有设置密码
  6. 我在iPhone 5S上测试了这个,首先它返回true,然后我在设置中禁用了密码,并返回false。最后,我重新启用了密码并返回true。先前的OS版本将返回false。代码在模拟器中工作,在设置了OS X密码的机器上返回true(我没有测试过替代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下并加密该文件会更好,如果用户决定禁用他们的密码,这将是不可恢复的。)

答案 2 :(得分:3)

Apple没有提供确定用户是否设置了密码的方法。

如果您的应用需要加密,您应该考虑使用受信任的加密实施加密和解密文件,并提示用户输入密码或将密钥存储在钥匙串中。

答案 3 :(得分:1)

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

} 

答案 4 :(得分:0)

iOS 9 以来, LocalAuthentication 框架中有一个标志 LAPolicyDeviceOwnerAuthentication

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

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

    if(passcodeEnabled) {
        return YES;
    }

    return NO;
}

答案 5 :(得分:0)

Swift 3

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

需要iOS 9或更高版本。