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;
PS 正如 Apple 在 WWDC 视频中指出的那样(711 钥匙串和 Touch ID 身份验证),他们选择不故意通过 API 直接提供密码状态,以防止应用程序进入不应出现的情况是(即“这个设备有密码吗?好的,太好了,我会以纯文本格式存储这个私人信息”。最好创建一个加密密钥,将其存储在该文件下kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
并加密该文件,这将是不可恢复的如果用户决定禁用他们的密码)。