• iOS keyChain 的使用


    详细资料,请参看苹果官方文档Keychain Services Reference 。

    ios中的keychain,用于保存用户的机密信息,对keychain的操作有4种,就是 增,删,改,查:

    SecItemCopyMatching

    Returns one or more keychain items that match a search query, or copies attributes of specific keychain items. 

    SecItemAdd
    Adds one or more items to a keychain.

    SecItemUpdate
    Modifies items that match a search query.

    SecItemDelete
    Deletes items that match a search query. 


    每个操作,都需要定义相应的CFDictionary,下面看看如何定义。

    操作函数需要的CFDictionary中的信息,介绍如下:

    A dictionary containing an item class key-value pair (“Keychain Item Class Keys and Values” (page 11)) and optional attribute key-value pairs (“Attribute Item Keys and Values” (page 16)) specifying the item's attribute values. 

    上边提到了2种key-value pair,一种是 item class key-value pair,一种是optional attribute key-value pair,首先要确定item所用的class,之后根据不同的class会用到不同的attribute。

    item class key-value pair,它被描述成Keychain Item Class Keys and Values。它的key是kSecClass ,它的value有5种:

    kSecClassGenericPassword,//存储普通的密码,
    kSecClassInternetPassword,//存储网络密码
    kSecClassCertificate,//存储证书,证书中包含共有密匙
    kSecClassKey,//存储密匙,其实就是私有密匙
    kSecClassIdentity,//存储Identity item,包括一个证书和一个密匙
    这5种value都有相应的attribute的key-value pair 可以使用,请参看官方文档。这些attribute key-value pair 的key都是固定的常量; 他们的value,有的是常量, 有的是NSString之类的变量,可以写入自定义内容。另外,这些attribute key-value pair 有的指定了检索的条件,有的指定了检索的返回值,等等,功能不同。
    
    
    下面看看如何添加一个普通密码到keychain中
    - (void)addIdentityIntoKeyChain
    { 
        OSStatus sanityCheck = noErr;    
    
        NSDictionary *dic = [NSDictionarydictionaryWithObjectsAndKeys: 
    
                             (__bridge id)(kSecClassGenericPassword) ,kSecClass,
    
                             @"this a my description",kSecAttrDescription,
    
                             [@"1234"dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
    
                    //        [@"5678" dataUsingEncoding:NSUTF8StringEncoding], kSecValueRef,
    
                             nil];    
    
        NSLog(@"add dic is %@",dic);   
    
        CFDictionaryRef dicRef = ( __bridgeCFDictionaryRef)dic;    
    
        sanityCheck = SecItemAdd(dicRef, nil);
    
        NSLog(@"%ld",sanityCheck);    
    
    }

    再看看针对上面这个item的2种不同的查询,

    第一种,查询具体的密码,即我们真正需要保存的东西

    - (void)getPass2
    {
        NSLog(@"pass 2===========");    
    
        NSDictionary *queryDictionary = [NSDictionarydictionaryWithObjectsAndKeys:
    
                                         @"this a my description",kSecAttrDescription,
    
                                         kCFBooleanTrue, kSecReturnData,
    
                                         kSecClassGenericPassword, kSecClass,
    
                                         nil];    
    
        CFTypeRef handle = NULL;
    
        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &handle);    
    
        NSLog(@"status is %ld",status);    
    
        if (status == noErr) {
    
            NSData *data = (__bridge NSData *)handle;
    
            NSString *str = [[NSStringalloc] initWithData:data encoding:NSUTF8StringEncoding];
    
            NSLog(@"str is %@",str);        
    
        }    
    
    }

    第二种,查询这个item的各种属性,不查询具体的密码

    - (NSString *)getPasswordFromKeyChain
    
    {
    
        NSLog(@"pass 1===========");
    
        NSDictionary *queryDictionary = [NSDictionarydictionaryWithObjectsAndKeys:
    
                                        @"this a my description",kSecAttrDescription,
    
                                        kCFBooleanTrue, kSecReturnAttributes,
    
                                        kSecClassGenericPassword, kSecClass,
    
                                        nil];   
    
        CFTypeRef handle = NULL;
    
        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &handle);    
    
        NSLog(@"status is %ld",status);    
    
        if (status == noErr) {
    
           NSDictionary *dic = (__bridge_transfer NSDictionary*) handle ;
    
            NSLog(@"dic is %@",dic);        
    
        }    
    
       return@"";
    
    } 

    请查看具体的输出,来理解这2中查询的不同。 

    2014-03-14 10:50:15.338 KeyChainTest[25074:907] add dic is {
        class = genp;
        desc = "this a my description";
        "v_Data" = <31323334>;
    }
    2014-03-14 10:50:15.356 KeyChainTest[25074:907] -25299
    2014-03-14 10:50:15.359 KeyChainTest[25074:907] pass 1===========
    2014-03-14 10:50:15.369 KeyChainTest[25074:907] status is 0
    2014-03-14 10:50:15.376 KeyChainTest[25074:907] dic is {
        acct = "";
        agrp = "BDN8QNY54S.com.Kings.test.KeyChainTest";
        cdat = "2013-10-16 06:02:54 +0000";
        desc = "this a my description";
        mdat = "2013-10-16 06:02:54 +0000";
        pdmn = ak;
        svce = "";
    }
    2014-03-14 10:50:15.381 KeyChainTest[25074:907] pass 2===========
    2014-03-14 10:50:15.389 KeyChainTest[25074:907] status is 0
    2014-03-14 10:50:15.393 KeyChainTest[25074:907] str is 1234

    另外,在keychain的使用中,还经常涉及一个问题,就是2个程序共享机密信息。这常常发生在以下情况:大型公司专门写了一个用户认证程序,指定的机器可以装上这个程序,用这个程序时要求输入帐号和密码,验证通过后,程序会从服务器取公司的证书和用户的私有密匙,并将他们加入到程序的keychain中 。之后该公司写的其他程序,如果需要帐号和密码,或者证书进行服务器验身份证时,就可以用刚才提到的用户认证程序在keychain中保存的信息。

    ios程序默认的keychain是不能够和其他程序共享的,如果想共享自己的信息,那么需要加入到一个Keychain Access Groups中,xcode5在工程设定中可以添加。主要注意的是这个Keychain Access Groups的名字是有限制的,必须和你的provisioning profile向一致,这就保证不可能访问到其他公司的机密信息。

    转一些别人的心得:

    1.相同bundle下生成的程序都可以共享相同group的keyChain.(我对这一条的理解不太一样,我感觉是这样的:bundle Name 分别是 com.companyName.A 和 com.companyName.B,那么这两个程序的keychain group name 就必须是 com.companyName.xxxx)

    相同bundle解释下就是:比如:2个程序分别使用的provision对应bundle是com.jv.key1和com.jv.key2,那你配置文件肯定是{Identifer}.com.jv.{name},其中identifer是苹果生成的随机串号,可以在申请证书时看到,复制过来即可,name可以自己取,程序中指定属于哪个Group即可。

    2.如果你在 addkey时,没有指定group,则会默认添加你keychain-access-groups里第一个group(这是keychain group中经常写2个group的原因,简历一个自己的group,防止污染主要的keychain group),如果你没有设置Entitlements,则默认使用对应的程序的bundle name,比如com.jv.key1,表示只能给自己程序使用。

    3.如果你程序添加的group并不存在你的配置文件中,程序会奔溃,表示无法添加。因此你只能添加你配置文件中支持的keychain。

     

  • 相关阅读:
    3.30一周拾遗
    3.25周 一周拾遗
    自动加载以及Composer的实现
    MySQL 事务处理
    PHP代码实现3 [函数角度]
    PHP代码实现2 [从变量和数据的角度] 1
    PHP代码实现2 [从变量和数据的角度] 2
    vue 报错: [Vue warn]: Error in nextTick: "RangeError: Maximum call stack size exceeded" 很可能是你的name错了
    vue电商开发记录1—实现电商图片放大镜,移入放大效果
    vue图片点击放大预览v-viewer
  • 原文地址:https://www.cnblogs.com/breezemist/p/3372142.html
Copyright © 2020-2023  润新知