美文网首页OC基础
iOS keyChain 研究

iOS keyChain 研究

作者: wzf_taker | 来源:发表于2016-06-07 14:55 被阅读670次

    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

    相关文章

      网友评论

        本文标题: iOS keyChain 研究

        本文链接:https://www.haomeiwen.com/subject/nviddttx.html