简体   繁体   English

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

[英]How can I find out if the iPhone user currently has a passcode set and encryption enabled?

I'm writing an iPhone application that requires its data to be encrypted. 我正在编写一个需要加密数据的iPhone应用程序。 I've learned how to turn on encryption for files by setting the NSFileProtectionComplete attribute. 我已经学会了如何通过设置NSFileProtectionComplete属性来打开文件加密。 I also know how to check the iPhone version to make sure they are running iOS 4.0 or better. 我也知道如何检查iPhone版本以确保它们运行的​​是iOS 4.0或更高版本。

What I've realized though, that if the user has not chosen a passcode and has not specifically enabled data protection on the Settings > General > Passcade Lock screen then the data isn't actually protected at all. 我已经意识到,如果用户没有选择密码并且没有在设置>常规>密码锁屏幕上专门启用数据保护,那么数据实际上根本不受保护。

I'd like to pop up a warning and tell the user that they must enable a passcode and turn on data protection (which requires a backup and restore on pre-4 iPhones), and then exit the application if they do not have a passcode and data protection enabled. 我想弹出警告并告诉用户他们必须启用密码并启用数据保护(这需要在4前iPhone上进行备份和恢复),然后如果他们没有密码则退出应用程序并启用数据保护。 I can't figure out anyway to find out the state of these settings though. 我无论如何都无法弄清楚这些设置的状态。 All of the APIs I've found, such as "protectedDataAvailable" in UIApplication all pass with success if data protection is disabled. 我发现的所有API,例如UIApplication中的“protectedDataAvailable”,如果禁用数据保护,都会成功通过。

Disclaimer: This answer was valid until ios 4.3.3 免责声明:此答案有效期至ios 4.3.3

If data protection is turned on, a newly created file will have a nil NSFileProtectionKey by default. 如果启用了数据保护,则默认情况下新创建的文件将具有nil NSFileProtectionKey

If data protection is turned off, a newly created file will have a NSFileProtectionNone NSFileProtectionKey by default. 如果关闭数据保护,则默认情况下新创建的文件将具有NSFileProtectionNone NSFileProtectionKey

Thus, you could detect the presence of file protection with the following code: 因此,您可以使用以下代码检测是否存在文件保护:

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

iOS 8 (OS X Yosemite) introduced a new API/constant used to detect if a user's device has a passcode. iOS 8(OS X Yosemite)引入了一个新的API /常量,用于检测用户的设备是否有密码。

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly can be used to detect if a passcode is set on the device. kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly可用于检测设备上是否设置了密码。

The flow is: 流程是:

  1. Attempt to save a new item on the keychain with that attribute set 尝试使用该属性集在钥匙串上保存新项目
  2. If it succeeds that indicates that a passcode is currently enabled 如果成功则表示当前启用了密码
  3. If the password doesn't get saved, that indicates there is no passcode 如果密码未保存,则表示没有密码
  4. Cleanup the item, because if it is already on the keychain it will make an "add" fail, looking like the passcode is not set 清理项目,因为如果它已经在钥匙串上,它将使“添加”失败,看起来密码未设置

I have tested this on my iPhone 5S, first it returned true , then I disabled the passcode in settings, and it returned false . 我在我的iPhone 5S上测试了这个,首先它返回true ,然后我在设置中禁用了密码,它返回false Finally, I re-enabled the passcode and it returns true . 最后,我重新启用了密码,它返回true Prior OS versions will return false . 以前的OS版本将返回false The code works in simulator, returning true on a machine with OS X password set (I haven't tested alternate OS X scenarios). 代码在模拟器中工作,在设置了OS X密码的机器上返回true (我没有测试过替代OS X场景)。

Also see sample project here: https://github.com/project-imas/passcode-check/pull/5 另请参阅此处的示例项目: https//github.com/project-imas/passcode-check/pull/5

Finally, to my knowledge iOS 8 doesn't have a setting to disable data protection, so I assume this is all you need to guarantee encryption. 最后,据我所知,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;

PS As Apple points out in the WWDC video introducing this (711 Keychain and authentication with Touch ID), they chose not to make the passcode-status directly available via API on purpose, in order to prevent apps from getting in situations they shouldn't be (ie "Does this device have a passcode? Okay, great, I'll store this private info in plain text". It would be much better to create an encryption key, store it under kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly and encrypt that file, which will be unrecoverable if a user decides to disable their passcode). PS正如Apple在WWDC视频中指出的那样(711 Keychain和Touch ID认证),他们选择不通过API直接提供密码状态,以防止应用程序进入他们不应该处理的情况be(即“这个设备是否有密码?好吧,很好,我将以纯文本形式存储此私人信息”。创建加密密钥,将其存储在kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly并加密该文件会更好。如果用户决定禁用其密码,则无法恢复。

Apple does not provide a method to determine whether the user has a passcode set. Apple没有提供确定用户是否设置密码的方法。

If your app needs encryption, you should consider encrypting and decrypting the files with a trusted encryption implementation and either prompting the user for a passcode or storing the key in the keychain. 如果您的应用需要加密,则应考虑使用可信加密实现加密和解密文件,并提示用户输入密码或将密钥存储在钥匙串中。

Regardless NSDataWritingAtomic or NSDataWritingFileProtectionComplete, result is always the same for me. 无论NSDataWritingAtomic还是NSDataWritingFileProtectionComplete,结果对我来说总是一样的。 Weird behaviour, here's the code: 奇怪的行为,这是代码:

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

} 

Since iOS 9 , there is a flag LAPolicyDeviceOwnerAuthentication in LocalAuthentication framework. 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;
}

Swift 3 斯威夫特3

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

iOS 9 or greater required. 需要iOS 9或更高版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果尚未启用用户密码锁,我如何使用iOS磁盘加密保护数据? - How can I protect data using iOS on-disk encryption when the user passcode lock has not been enabled? 我可以判断一下iPhone是否有密码? - Can I tell if the iPhone has a passcode? 有没有一个简洁的方法来判断iPhone / iPad是否已被用户设置了密码 - Is there a neat way to tell if iPhone/iPad has been set a passcode by user iPhone 开发 - 我如何在运行时找出 UITableView 中当前显示的顶部单元格是什么? - iPhone dev - How can I find out at runtime what the top cell that's currently being displayed is in a UITableView? 如何在iPhone上找出当前使用的DNS名称服务器 - How to find out currently used DNS namesevers on iPhone 我如何找出用户在我的应用程序中为我的应用程序支付了多少钱? - How can I find out how much a user has paid for my app inside my app? 找出iPhone有多少内存? - Find out how much memory an iPhone has? 以编程方式在iPhone中放大和缩小:如何设置动画速度? - Programmatically zoom in and out in iPhone: how can i set animation speed? 在 iPhone 上确定用户是否启用了推送通知 - Determine on iPhone if user has enabled push notifications 如何创建密码视图iphone - How to create a passcode view iphone
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM