in iOS ~ read.

iOS - Data protect level

一、设置 Complete 保护等级
  • The default protect level for iOS file is NSFileProtectionCompleteUntilFirstUserAuthentication in iOS, it can be set to NSFileProtectionComplete which is the safest level. Because system will delete the hierarchy secret key in memory and set the file to unreadable.

  • 设置 NSData 的保护等级

NSData * data = [NSData new];  
NSError * err = nil;  
NSString * downloadPath = [NSString stringWithFormat:@"%@doc.pdf",NSTemporaryDirectory()];  
[data writeToFile:downloadPath options:NSDataWritingFileProtectionComplete error:&err];
  • 设置文件的保护等级
NSArray * searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true);  
NSString * applicationDirectory = [searchPaths lastObject];  
NSString * filePath = [applicationDirectory stringByAppendingString:@"myextension.txt"];  
NSError * er = nil;  
NSDictionary * attr = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];  
[[NSFileManager defaultManager]setAttributes:attr ofItemAtPath:filePath error:&er];
  • 设置 sqlite 的保护等级
NSString * databasePath = @"sqlite.db";  
sqlite3_open_v2([databasePath UTF8String], &handle, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN, NULL);  

二、设置 CompleteUnlessOpen 保护等级
  • 如果一个文件当前被应用打开,那会暂时禁用该文件的保护。CompleteUnlessOpen 会确保打开的文件在设备被锁定时依然能够写入,并且允许新建文件到磁盘。不过这个等级保护的文件锁屏时无法打开,除非锁屏时就已经提前打开。
  • 文件保护的过程
    • 生成一个文件秘钥来加密文件内容
    • 生成一个额外的秘钥对,用于生成文件公钥和文件私钥
    • 用文件私钥和Protected Unless Open 等级公钥计算出一个共享密码
    • 用共享密码SHA-1 散列值加密文件秘钥
    • 加密过的文件秘钥会存储在文件的元数据中,元数据中还有文件的公钥
    • 系统丢弃文件私钥
    • 关闭文件时从内存中删除未加密的文件密钥
    • 需要再次打开文件时,用Protected Unless Open 等级私钥和文件公钥计算共享密码
    • 计算共享密码的SHA1-1 散列值,把它当做解密文件的秘钥

DataProtectionClass 权限
  • 如果应用在进入后台或锁定时不需要写入或读取文件,可以在工程中配置 NSFileProtectionComplete 值来添加权限。这会确保所有受保护的文件数据只能在设备解锁时访问,相当于为所有的应用文件都设置kSecAttrAccessibleWhenUnlocked。(这会影响 NSFileManager NSData,SQLite 和CoreData,但是其他类型的文件不会收到保护(plist,缓存))。
  • 只要在 target capbilities 中打开DataProtection 开关即可。
检查保护数据是否可用
  • 对于设备锁定时还需要在后台工作的应用,在使用前必须判断保护的数据是否可用,有三种机制来实现这个目的:
  • 代理
//数据变不可用
-(void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application{
}
//数据可用
-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application{
}
  • 通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(data) name:UIApplicationProtectedDataWillBecomeUnavailable object:nil];
[[NSNotificationCenter defaultCenter]  addObserver:self selector:@selector(data) name:UIApplicationProtectedDataDidBecomeAvailable object:nil];
  • 方法
//查看数据是否可用
[[UIApplication sharedApplication] isProtectedDataAvailable];

使用 CommonCrypt 加密
//参数分别是控制算法,密钥长度,初始化向量、操作模式等。
    CCCrypt(CCOperation op, CCAlgorithm alg, CCOptions options, const void *key, size_t keyLength, const void *iv, const void *dataIn, size_t dataInLength, void *dataOut, size_t dataOutAvailable, size_t *dataOutMoved);
  • 获取随机字节
uint8_t randomdata[16];  
int result = SecRandomCopyBytes(kSecRandomDefault, 16, (uint8_t*)randomdata);  

执行散列操作
  • 比较两块数据是否匹配,通常用来验证文件版本,还可以验证自身是否包含敏感信息。如果对字符串执行散列操作,可以使用CC_SHA家族方法。
    const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:str.length];
    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(data.bytes, (int)data.length, digest);
  • 计算 HMAC确保消息可靠性
//散列的密钥是一个UTF-8编码的字符串。
    NSData * key = [@"key" dataUsingEncoding:NSUTF8StringEncoding];
//一个UTF-8的字符串
    NSData * data = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
//用来存散列的值
    NSMutableData * hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
//传递参数
    CCHmac(kCCPRFHmacAlgSHA256, [key bytes], [key length], [data bytes], [data length], [hash mutableBytes]);
  • TouchID 进行本地验证
    LAContext * context = [LAContext new];
    NSError * e = nil;
    NSString * reason = @"a";
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&e]) {
        [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:^(BOOL success, NSError * _Nullable error) {
            if (success) {
                NSLog(@"right");
            } else {
                NSLog(@"false");
            }
        }];
    } else {
        NSLog(@"err = %@",[e userInfo]);
    }
comments powered by Disqus