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

Mik*_*ike 38 iphone encryption data-protection core-data ios4

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

我已经意识到,如果用户没有选择密码并且没有在设置>常规>密码锁屏幕上专门启用数据保护,那么数据实际上根本不受保护.

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

Hea*_*ers 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]];
Run Code Online (Sandbox Code Playgroud)

  • 这个方法也不适用于我的iPad 5.0.1,总是返回true. (3认同)
  • 在iOS 5.0/5.0.1上看起来这种行为可能已经改变.使用示例代码,无论iPad2上的密码设置如何,我都会获得NSFileProtectionNone. (2认同)

owe*_*nfi 13

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

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

流程是:

  1. 尝试使用该属性集在钥匙串上保存新项目
  2. 如果成功则表示当前启用了密码
  3. 如果密码未保存,则表示没有密码
  4. 清理项目,因为如果它已经在钥匙串上,它将使"添加"失败,看起来密码未设置

我已经在我的iPhone 5S上测试了这个,首先它返回了true,然后我在设置中禁用了密码,然后它返回了false.最后,我重新启用了密码并返回true.以前的OS版本将返回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;
Run Code Online (Sandbox Code Playgroud)

PS正如Apple在WWDC视频中指出的那样(711 Keychain和Touch ID认证),他们选择不通过API直接提供密码状态,以防止应用程序进入他们不应该处理的情况be(即"这个设备是否有密码?好吧,很好,我将以纯文本形式存储此私人信息".创建加密密钥,存储kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly并加密该文件会更好,这将是不可恢复的如果用户决定禁用他们的密码).