钥匙串,实际上是一个加密后的数据库,如下图所示。
即使吧App删除,钥匙串里面的数据也不会丢失。
数据都是以 Item 的形式来存储的,每个 Item 由一个加密后的 Data 数据,还有一系列用来描述该 Item 属性的 Attributes 组成。
由于是数据库,关键方法只有四种,增删改查,对应的是
- SecItemAdd
- SecItemDelete
- SecItemUpdate
- SecItemCopyMatching
下面简单讲述一下使用方法
SecItemAdd
CFTypeRef result;
NSDictionary *query = @{
// 一个典型的新增方法的参数,包含三个部分
// 1.kSecClass key,它用来指定新增对象的类型
(NSString *)kSecClass: (NSString *)kSecClassGenericPassword,
// 2.若干项属性 key,例如 kSecAttrAccount,kSecAttrLabel 等,用来描述新增对象的属性
(NSString *)kSecAttrAccount: @"uniqueID",
// 3.kSecValueData key,用来设置新增对象保存的数据
(NSString *)kSecValueData: [@"token" dataUsingEncoding:NSUTF8StringEncoding],
// 可选
// 如果需要获取新增的 Item 对象的属性,需要如下属性,
(NSString *)kSecReturnData: (NSNumber *)kCFBooleanTrue,
(NSString *)kSecReturnAttributes: (NSNumber *)kCFBooleanTrue,
};
OSStatus status = SecItemAdd((CFDictionaryRef)query, &result);
if (result == errSecSuccess) {
// 新增成功
NSDictionary *itemInfo = (__bridge NSDictionary *)result;
NSLog(@"info: %@", itemInfo);
} else {
// 其他错误
}
result类型判断方式
kSecReturnData | kSecReturnAttributes | result |
---|---|---|
true | true | NSDictionary(包含属性与数据) |
true | false | NSData(只有数据) |
false | true | NSDictionary(只包含属性) |
false | false | null |
SecItemDelete
NSDictionary *query = @{
// 一个典型的删除方法的参数,包含两个部分
// 1、kSecClass key,它用来指定删除对象的类型,必填。
(NSString *)kSecClass: (NSString *)kSecClassGenericPassword,
// 2、若干项属性 key,可选。
// 例如 kSecAttrAccount,kSecAttrLabel 等,用来设置删除对象的范围
// 默认情况下,符合条件的全部 Item 都会被删除
(NSString *)kSecAttrAccount: @"uniqueID",
};
OSStatus status = SecItemDelete((CFDictionaryRef)query);
if (result == errSecSuccess) {
// 删除成功
} else {
// 其他错误
}
SecItemUpdate
// 1、找出需要更新属性的 Item
// 参数格式与 SecItemCopyMatching 方法中的参数格式相同
NSDictionary *query = @{
(NSString *)kSecClass: (NSString *)kSecClassGenericPassword,
(NSString *)kSecAttrAccount: @"uniqueID",
};
// 2、需要更新的属性
// 若干项属性 key
NSDictionary *update = @{
(NSString *)kSecAttrAccount: @"another uniqueID",
(NSString *)kSecValueData: @"another value",
};
OSStatus status = SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)update);
if (result == errSecSuccess) {
// 更新成功
} else {
// 其他错误
}
SecItemDelete
NSDictionary *query = @{
// 一个典型的删除方法的参数,包含两个部分
// 1、kSecClass key,它用来指定删除对象的类型,必填。
(NSString *)kSecClass: (NSString *)kSecClassGenericPassword,
// 2、若干项属性 key,可选。
// 例如 kSecAttrAccount,kSecAttrLabel 等,用来设置删除对象的范围
// 默认情况下,符合条件的全部 Item 都会被删除
(NSString *)kSecAttrAccount: @"uniqueID",
};
OSStatus status = SecItemDelete((CFDictionaryRef)query);
if (result == errSecSuccess) {
// 删除成功
} else {
// 其他错误
}
SecItemCopyMatching
CFTypeRef result;
NSDictionary *query = @{
// 一个典型的搜索方法的参数,包含三个部分
// 1、kSecClass key(必填),它用来指定搜索对象的类型
(NSString *)kSecClass: (NSString *)kSecClassGenericPassword,
// 2、若干项属性 key(可选),例如 kSecAttrAccount,kSecAttrLabel 等,用来描述搜索对象的属性
(NSString *)kSecAttrAccount: @"uniqueID",
// 3、搜索属性(可选)
// 例如 kSecMatchLimit(搜索一个还是多个,影响返回结果类型)
// kSecMatchCaseInsensitive 是否大小写敏感等
(NSString *)kSecMatchLimit: (NSString *)kSecMatchLimitAll,
(NSString *) kSecMatchCaseInsensitive: (NSNumber *) kCFBooleanTrue,
// (可选)如果需要获取新增的 Item 对象的属性,需要如下属性,
(NSString *)kSecReturnData: (NSNumber *)kCFBooleanTrue,
(NSString *)kSecReturnAttributes: (NSNumber *)kCFBooleanTrue,
};
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, &result);
if (result == errSecSuccess) {
// 新增成功
NSDictionary *itemInfo = (__bridge NSDictionary *)result;
NSLog(@"info: %@", itemInfo);
} else {
// 其他错误
}
result类型判断方式
kSecReturnData | kSecReturnAttributes | kSecMatchLimit | result |
---|---|---|---|
true | true | kSecMatchLimitAll | NSDictionary(包含属性与数据)数组 |
true | true | kSecMatchLimitOne | NSDictionary(包含属性与数据)对象 |
true | false | kSecMatchLimitAll | NSData(只有数据)数组 |
true | false | kSecMatchLimitOne | NSData(只有数据)对象 |
false | true | kSecMatchLimitAll | NSDictionary(只包含属性)数组 |
false | true | kSecMatchLimitOne | NSDictionary(只包含属性)对象 |
false | false | kSecMatchLimitAll | 空数组 |
false | false | kSecMatchLimitOne | null |
参考文档
https://developer.apple.com/documentation/security/keychain_services/keychain_items?language=occ
Demo 地址
https://github.com/huangrrui/Key-chain