Keychain Services -- kSecClassGe

2017-12-20  本文已影响34人  大鹏鸟

今天做一个需求,要求获取到设备的IDFA,但是我们都知道,这个值是会变的,会受到用户的影响,所以就想看能不能做一个持久化(对于用户不允许的情况,可以自己生成一个,实现有具体的开源代码),随着软件的卸载和再安装,这个值始终是不变的。
就想起来了keychain--这个系统级的存储。这也就是为什么有些软件在卸载后重新安装后用户名和密码还在的方法之一(因为还有其他方法)。

除了做数据存储,其实它还可以做APP间的数据共享。

因为这部分概念的东西并不多,所以下面将直接上代码,遇到问题顺带着再说;另:mac和iOS不同,这里只说iOS的。

所有的详细资料在这里

一、Keychain

1、概念

Keychain是一个储存在文件下的简单数据库。通常情况下,app里有一个简单的keychain,可以被所有的app使用。
Keychain有任意数量的钥匙链(item),该钥匙链里包含一组属性。该属性和钥匙链的类型相关。创建日期和label对所有的钥匙链是通用的。其他的都是根据钥匙链的类型不同而不同,比如,generic password类型包含service和account属性。
钥匙链可以使用kSecAttrSynchronizable同步属性,被标记为该属性的值都可以被放置在iCloud的钥匙链中,它会被自动同步到相同账号的设备上。
有些钥匙链需要保护起来,比如密码和私人key,都会被加密;对于那些不需要被保护的钥匙链,比如证书,就不会被加密。
在iOS设备上(手机),当屏幕被解锁时,钥匙链的访问权限就会被打开。

2、访问

首先说明其能保存的类型,有5种:
(1)添加
- (void)saveBasicInfo {
    CFMutableDictionaryRef mutableDicRef = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(mutableDicRef, kSecClass, kSecClassGenericPassword);  //类型
    CFDateRef dateRef = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
    CFDictionarySetValue(mutableDicRef, kSecAttrCreationDate, dateRef);  //创建时间
    CFStringRef strRef = CFSTR("save generic password 2");
    CFDictionarySetValue(mutableDicRef, kSecAttrDescription, strRef);  //描述
    CFStringRef commentStrRef = CFSTR("generic password comment 2");
    CFDictionarySetValue(mutableDicRef, kSecAttrComment, commentStrRef); //备注、注释
    CFNumberRef creatorRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, "zhou");
    CFDictionarySetValue(mutableDicRef, kSecAttrCreator, creatorRef);  //创建者,只能是四个字符长度
    CFNumberRef typeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, "type");
    CFDictionarySetValue(mutableDicRef, kSecAttrType, typeRef);   // 类型
    CFStringRef labelRef = CFSTR("label 2");
    CFDictionarySetValue(mutableDicRef, kSecAttrLabel, labelRef); //标签,用户可以看到
    CFDictionarySetValue(mutableDicRef, kSecAttrIsInvisible, kCFBooleanTrue);  //是否不可见、是否隐藏(kCFBooleanTrue、kCFBooleanFalse)
    CFDictionarySetValue(mutableDicRef, kSecAttrIsNegative, kCFBooleanFalse); //标记是否有密码(kCFBooleanTrue、kCFBooleanFalse)
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(mutableDicRef, kSecAttrAccount, accountRef);  //账户,相同的账户不允许储存两次,否则会报错
    CFStringRef serviceRef = CFSTR("service");
    CFDictionarySetValue(mutableDicRef, kSecAttrService, serviceRef);  //所具有的服务
    CFMutableDataRef genericRef = CFDataCreateMutable(kCFAllocatorDefault, 0);
    char * generic_char = "personal generic 2";
    CFDataAppendBytes(genericRef, (const UInt8 *)generic_char, sizeof("personal generic 2"));
    CFDictionarySetValue(mutableDicRef, kSecAttrGeneric, genericRef);  //用户自定义
    CFDictionarySetValue(mutableDicRef, kSecValueData, genericRef);  //保存值
    OSStatus status = SecItemAdd(mutableDicRef, nil);  //相同的东西只能添加一次,不能重复添加,重复添加会报错
    if (status == errSecSuccess) {
        NSLog(@"success");
    } else {
        NSLog(@"%@",@(status));
    }
... //神略的是前面创建的变量的释放
}
(2)查找(最好的办法是根据用户名查找)
- (OSStatus)keychainMatching {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    //查找类型
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    //匹配的属性 越详细,越精准
//    CFStringRef strRef = CFSTR("save generic password");
//    CFDictionarySetValue(queryDic, kSecAttrDescription, strRef);  //描述
//    CFStringRef commentStrRef = CFSTR("generic password comment");
//    CFDictionarySetValue(queryDic, kSecAttrComment, commentStrRef); //备注、注释
//    CFNumberRef creatorRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, "zhou");
//    CFDictionarySetValue(queryDic, kSecAttrCreator, creatorRef);  //创建者,只能是四个字符长度
//    CFNumberRef typeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, "type");
//    CFDictionarySetValue(queryDic, kSecAttrType, typeRef);   // 类型
//    CFStringRef labelRef = CFSTR("label");
//    CFDictionarySetValue(queryDic, kSecAttrLabel, labelRef); //标签,用户可以看到
    //查找的参数
//    CFDictionarySetValue(queryDic, kSecMatchLimit, kSecMatchLimitAll);  //可以控制当key=kSecReturnAttributes时返回值的个数
    //返回类型
//    CFDictionarySetValue(queryDic, kSecReturnData, kCFBooleanTrue);
    CFDictionarySetValue(queryDic, kSecReturnAttributes, kCFBooleanTrue);
    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching(queryDic, &result);
    if (status == errSecSuccess) {
        NSLog(@"success:%@",result);
        if (CFGetTypeID(result) == CFDictionaryGetTypeID()) { //类型判断
            NSLog(@"Dictionary");
        }
    } else {
        NSLog(@"%@",@(status));
    }
...//神略的是前面创建的变量的释放
}
(3)删除
方法一:
//直接删除
- (void)deleteItemDirect {
   //删除CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
   CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
   CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);  //这个不要丢!!!
   CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
   CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);
   if (accountRef) {
       CFRelease(accountRef);
   }
   OSStatus delStatus = SecItemDelete(queryDic);
   if (delStatus == errSecSuccess) {
       NSLog(@"delete success");
   } else {
       NSLog(@"%@",@(delStatus));
   }
}
方法二:
//先查找,再删除
- (void)deleteItemWithQuery {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);
    if (accountRef) {
        CFRelease(accountRef);
    }
    
    CFDictionarySetValue(queryDic, kSecReturnAttributes, kCFBooleanTrue);
    CFDictionarySetValue(queryDic, kSecReturnRef, kCFBooleanTrue);  //这一句话必须要有,否则删除不了
//    CFDictionarySetValue(queryDic, kSecReturnPersistentRef, kCFBooleanTrue);  //不能用这句,这句是做什么用的呢?
    CFTypeRef result = NULL;
    OSStatus queryStatus = SecItemCopyMatching(queryDic, &result);
    if (queryDic) {
        CFRelease(queryDic);
    }
    if (queryStatus != errSecSuccess) {
        NSLog(@"query failed");
        return;
    }
    if (CFGetTypeID(result) == CFDictionaryGetTypeID()) {
        OSStatus delStatus = SecItemDelete(result);
        if (delStatus == errSecSuccess) {
            NSLog(@"delete success");
        } else {
            NSLog(@"delete failed:%@",@(delStatus));
        }
    } else if (CFGetTypeID(result) == CFArrayGetTypeID()) {
        CFArrayRef arrRef = result;
        for (CFIndex i = 0; i < CFArrayGetCount(arrRef); i++) {
            OSStatus delStatus = SecItemDelete(CFArrayGetValueAtIndex(arrRef, i));
            if (delStatus == errSecSuccess) {
                NSLog(@"delete success");
            } else {
                NSLog(@"delete failed:%@",@(delStatus));
            }
        }
    }
}
(4)、更新
方法一:
//更新
- (void)updateItem {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);
    if (accountRef) {
        CFRelease(accountRef);
    }
    CFMutableDictionaryRef updateDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFStringRef strRef = CFSTR("save generic password update");
    CFDictionarySetValue(updateDic, kSecAttrDescription, strRef);  //描述
    OSStatus updateStatus = SecItemUpdate(queryDic, updateDic);
    if (updateStatus == errSecSuccess) {
        NSLog(@"success");
    } else {
        NSLog(@"update Failed:%@",@(updateStatus));
    }
    CFRelease(queryDic);
    CFRelease(updateDic);
}
方法二:
- (void)updateItemAfterSearch {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);
    if (accountRef) {
        CFRelease(accountRef);
    }
    
    CFDictionarySetValue(queryDic, kSecReturnAttributes, kCFBooleanTrue);
    CFDictionarySetValue(queryDic, kSecReturnRef, kCFBooleanTrue);  //这一句话必须要有,否则删除不了
    //    CFDictionarySetValue(queryDic, kSecReturnPersistentRef, kCFBooleanTrue);  //不能用这句,这句是做什么用的呢?
    CFTypeRef result = NULL;
    OSStatus queryStatus = SecItemCopyMatching(queryDic, &result);
    if (queryDic) {
        CFRelease(queryDic);
    }
    if (queryStatus != errSecSuccess) {
        NSLog(@"query failed");
        return;
    }
    if (CFGetTypeID(result) != CFDictionaryGetTypeID()) {
        return;
    }
    CFMutableDictionaryRef updateDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFStringRef strRef = CFSTR("save generic password update with query");
    CFDictionarySetValue(updateDic, kSecAttrDescription, strRef);  //描述
    OSStatus updateStatus = SecItemUpdate(result, updateDic);
    if (updateStatus == errSecSuccess) {
        NSLog(@"success");
    } else {
        NSLog(@"update Failed:%@",@(updateStatus));
    }
    CFRelease(updateDic);
}
(5)获取值
- (OSStatus)keychainMatching {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    //查找类型
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);  //账户
    //查找的参数
    CFDictionarySetValue(queryDic, kSecMatchLimit, kSecMatchLimitOne);  //可以控制当key=kSecReturnAttributes时返回值的个数
    //返回类型
    CFDictionarySetValue(queryDic, kSecReturnAttributes, kCFBooleanTrue);
    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching(queryDic, &result);
    if (status == errSecSuccess) {
        NSLog(@"success:%@",result);
        if (CFGetTypeID(result) == CFDictionaryGetTypeID()) { //类型判断
            NSLog(@"label:%@",CFDictionaryGetValue(result, kSecAttrDescription));
        }
    } else {
        NSLog(@"%@",@(status));
    }
    if (queryDic) {
        CFRelease(queryDic);
    }
    return status;
}
- (OSStatus)keychainMatching {
    CFMutableDictionaryRef queryDic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    //查找类型
    CFDictionarySetValue(queryDic, kSecClass, kSecClassGenericPassword);
    CFStringRef accountRef = CFSTR("zhoupengzu_basic 2");
    CFDictionarySetValue(queryDic, kSecAttrAccount, accountRef);  //账户
    //查找的参数
    //返回类型
    CFDictionarySetValue(queryDic, kSecReturnData, kCFBooleanTrue);
    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching(queryDic, &result);
    if (status == errSecSuccess) {
        NSLog(@"success:%@",result);
        if (CFGetTypeID(result) == CFDictionaryGetTypeID()) { //类型判断
            NSLog(@"label:%@",CFDictionaryGetValue(result, kSecAttrDescription));
        } else if (CFGetTypeID(result) == CFDataGetTypeID()) {
            const UInt8 * str = CFDataGetBytePtr(result);
            NSLog(@"%s",str);
        }
    } else {
        NSLog(@"%@",@(status));
    }
    if (queryDic) {
        CFRelease(queryDic);
    }
    return status;
}

以上代码在这里

上一篇 下一篇

猜你喜欢

热点阅读