我正在编写一个需要加密数据的iPhone应用程序。我已经学会了如何通过设置NSFileProtectionComplete属性来打开文件加密。我也知道如何检查iPhone版本以确保它们运行的是iOS 4.0或更高版本。
我已经意识到,如果用户没有选择密码并且没有在设置>上专门启用数据保护。一般> Passcade Lock屏幕然后数据实际上没有受到保护。
我想弹出一个警告并告诉用户他们必须启用密码并打开数据保护(这需要在4前iPhone上备份和恢复),然后退出应用程序,如果他们不启用密码和数据保护。我无论如何都无法弄清楚这些设置的状态。我发现的所有API,例如UIApplication中的“protectedDataAvailable”,如果禁用数据保护,都会成功通过。
答案 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
可用于检测设备上是否设置了密码。
流程是:
我在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.deviceOwnerAuthentication, error:nil)
}
需要iOS 9或更高版本。