iOS keyChain 研究

http://blog.csdn.net/jerryvon/article/details/16843065

一.基本知识
1.方法
SecItemAdd 增
SecItemUpdate 改
SecItemDelete 删
SecItemCopyMatching 查

2.权限
文档上说iOS的keyChain是一个相对独立的空间,当程序替换,删除时并不会删除keyChain的内容,这个要比Library/Cache好。刷机,恢复出厂应该就没有了。关于备份,只会备份数据,到那时不会备份设备的密钥,换句话说,即使拿到数据,也没有办法解密里面的内容。有人说似乎破解的手机就能破解keyChain,本人并不清楚,希望有大神能指教。但个人认为,keyChain只是沙盒的升级版,可以存放一些非私密的信息,即使破解也不影响其它用户,只影响那个破解了的设备。(比如针对该设备的一个密钥)。

可访问性一般来说,自己的程序只能访问自己的keychain,相同bundle的程序通过设置group可以互相共享同组的keychain,从而实现程序间可以共同访问一些数据。详细后面介绍一些我测试下来的经验。

3.如何查询keyChain

[objc] view plain copy

print?
genericPasswordQuery = [[NSMutableDictionary alloc] init];
[genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];//1
[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];//2
******if** (accessGroup !=** nil){
[genericPasswordQuery setObject:accessGroup forKey:(
id)kSecAttrAccessGroup];//3
}
[genericPasswordQuery setObject:(
id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];//4
[genericPasswordQuery setObject:(
id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];//5
******NSDictionary
tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
******NSMutableDictionary
* outDictionary =* nil;
******if
(SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef )&outDictionary) == noErr){//6
//found and outDicitionary is not nil
}
else*{
//not found
}

1.设置Class值,每个Class对应的都有不同的参数类型

2.用户确定的参数,一般是程序中使用的类别,比如说是"Password"或"Account Info",作为search的主力条件
3.设置Group,如果不同程序都拥有这个组,那么不同程序间可以共享这个组的数据
4.只返回第一个匹配数据,查询方法使用,还有值kSecMatchLimitAll
5.返回数据为CFDicitionaryRef,查询方法使用
6.执行查询方法,判断返回值
eg:这个是none-ARC的代码哦!ARC情况下会有bridge提示。

4.类型转换
介绍增删改方法调用前,先介绍转换方法,如何将NSDictionary转换成KeyChain方法可以设置的Dicitionary,一般在写程序过程中,应该尽量避免直接访问KeyChain,一般会创建一个NSDictionary来同步对应的数据,所以两者需要做转换。

[objc] view plain copy

print?
//data to secItem

  • (NSMutableDictionary )dictionaryToSecItemFormat:(NSDictionary* *)dictionaryToConvert
    {
    // Create a dictionary to return populated with the attributes and data.
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

    //设置kSecClass
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    //将Dictionary里的kSecValueData(一般就是这个keyChain里主要内容,比如说是password),NSString转换成NSData
    NSString passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];
    [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(
    id*)kSecValueData];
    return returnDictionary;
    }
    //secItem to data

  • (NSMutableDictionary )secItemFormatToDictionary:(NSDictionary* *)dictionaryToConvert
    {
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

    // Add the proper search key and class attribute.
    [returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

    // Acquire the password data from the attributes.
    NSData passwordData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (
    CFTypeRef* )&passwordData) == noErr)
    {
    // 删除多余的kSecReturnData数据
    [returnDictionary removeObjectForKey:(
    id*)kSecReturnData];

      // 对应前面的步骤,将数据从NSData转成NSString  
      **NSString** *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length]  
                                                   encoding:NSUTF8StringEncoding] autorelease];  
      [returnDictionary setObject:password forKey:(**id**)kSecValueData];  
    

    }
    else
    {
    NSAssert(NO, @"Serious error, no matching item found in the keychain.\n");
    }
    [passwordData release];
    return returnDictionary;
    }

5.增删改
用代码来说明

[objc] view plain copy

print?

  • (void)writeToKeychain
    {
    NSDictionary attributes = NULL;
    NSMutableDictionary updateItem = NULL;
    OSStatus result;
    //判断是增还是改
    if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (
    CFTypeRef** )&attributes) == noErr)
    {
    // First we need the attributes from the Keychain.
    updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
    // Second we need to add the appropriate search key/values.
    [updateItem setObject:[genericPasswordQuery objectForKey:(
    id)kSecClass] forKey:(id)kSecClass];
    // Lastly, we need to set up the updated attribute list being careful to remove the class.
    NSMutableDictionary tempCheck = [
    self** dictionaryToSecItemFormat:keychainItemData];
    //删除kSecClass update不能update该字段,否则会报错
    [tempCheck removeObjectForKey:(id)kSecClass];
    //参数1表示search的,参数2表示需要更新后的值
    result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
    }else{
    //增加
    result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
    }
    }

删除很简单,就不写注释了
[objc] view plain copy

print?

  • (void)resetKeychainItem
    {
    OSStatus junk = noErr;
    if (!keychainItemData)
    {
    self.keychainItemData = [[NSMutableDictionary alloc] init];
    }
    else if (keychainItemData)
    {
    NSMutableDictionary tempDictionary = [self* dictionaryToSecItemFormat:keychainItemData];
    junk = SecItemDelete((CFDictionaryRef)tempDictionary);
    NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." );
    }

    // Default attributes for keychain item.
    [keychainItemData setObject:@"" forKey:(id)kSecAttrAccount];
    [keychainItemData setObject:@"" forKey:(id)kSecAttrLabel];
    [keychainItemData setObject:@"" forKey:(id)kSecAttrDescription];

    // Default data for keychain item.
    [keychainItemData setObject:@"" forKey:(id)kSecValueData];
    }

二.Group的配置
配置Target的Code Signing Entitlements.

配置该文件

可以配置一个Array列表,表示该程序可以支持多个group
这样就可以在创建secItem时候添加kSecAttrAccessGroup了。
经过测试有以下经验同大家分享:
1.相同bundle下生成的程序都可以共享相同group的keyChain.
相同bundle解释下就是:比如:2个程序分别使用的provision对应bundle是com.jv.key1和com.jv.key2,那你配置文件肯定是{Identifer}.com.jv.{name},其中identifer是苹果生成的随机串号,可以在申请证书时看到,复制过来即可,name可以自己取,程序中指定属于哪个Group即可。
2.如果你在 addkey时,没有指定group,则会默认添加你keychain-access-groups里第一个group,如果你没有设置Entitlements,则默认使用对应的程序的bundle name,比如com.jv.key1,表示只能给自己程序使用。
3.如果你程序添加的group并不存在你的配置文件中,程序会奔溃,表示无法添加。因此你只能添加你配置文件中支持的keychain。

参考资料:
苹果文档:
Keychain Services Reference

Certificate, Key, and Trust Services Programming Guide
Keychain Services Programming Guide

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容