zoukankan      html  css  js  c++  java
  • iOS Keychain钥匙串,应用间数据共享打造iOS上的全家桶

    Demo先行:https://github.com/rayshen/GIKeychainGroupDemo

    该demo里有2个工程,你先运行任何一个会存储一个值,再运行另一个会访问之前的app存储的值,并修改。

    官方:https://developer.apple.com/library/ios/samplecode/GenericKeychain/Introduction/Intro.html

    之前博客使用过Keychain,实现了数据删除APP后还能保存,但是并没有实现APP间的共享。

    实现APP间的数据共享,主要依赖于在数据存入钥匙串时,使用同一个钥匙串条目。

    主要分为两部分:

    1.赋予应用对某个钥匙串条目的访问权限。

    2.写入时配置钥匙串条目,对kSecAttrAccessGroup的值进行设置。

    一、APP对钥匙串的访问权限:

    (1)未对应用APP的entitlement(授权)进行配置时,APP使用钥匙串存储时,会默认存储在自身BundleID的条目下。

    (2)对APP的entitlement(授权)进行配置后,说明APP有了对某个条目的访问权限。

    钥匙串的可视化效果可参见Mac的APP-钥匙串访问。

    APP钥匙串访问权限的配置方法:(这里XXXXX模拟器随意,但真机必须为自己开发者账号ID,否则无法通过编译)

    1.新建一个Plist文件,在Plist中的数组中添加可以访问的条目的名字(如KeychainAccessGroups.plist),结构如下:

    Plist代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>keychain-access-groups</key>
        <array>
            <string>XXXXX.GrassInfoAppFamily</string>
        </array>
    </dict>
    </plist>

    2.在Build-setting中进行配置,搜索entitlement,注意路径别配置错:

    二、APP对钥匙串的操作:

    钥匙串的操作接口都位于Security.framework框架下,它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。

    其过程可以总结为:

    1.配置查询字典,格式是NSMutableDictionary,需要配置的内容下次再分析,功能就相当于写一句SQL一样。

    2.进行增(SecItemAdd)、删(SecItemDelete)、改(SecItemUpdate)、查(SecItemCopyMatching)。

    代码Demo里面有,这里以增为例,下面有2个语句,一个是增加到自身BundleID的钥匙串条目,一个是增加到共享的条目中。

    //创建一个基本的查询字典
    + (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
        return [NSMutableDictionary dictionaryWithObjectsAndKeys:
                (__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
                service, (__bridge id)kSecAttrService,
                service, (__bridge id)kSecAttrAccount,
                (__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
                nil];
    }
    
    + (void)addKeychainData:(id)data forKey:(NSString *)key{
        //Get search dictionary
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
        //Delete old item before add new item
        SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
        //Add new object to search dictionary(Attention:the data format)
        [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
        //Add item to keychain with the search dictionary
        SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
    }
    
    +(void)addShareKeyChainData:(id)data forKey:(NSString *)key{
        //Get search dictionary
        NSMutableDictionary *keychainQuery = [self getKeychainQuery:key];
        [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup];
        //Delete old item before add new item
        SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
        //Add new object to search dictionary(Attention:the data format)
        [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
        //Add item to keychain with the search dictionary
        SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
    }

    函数  [keychainQuery setObject:accessGroupItem forKey:(id)kSecAttrAccessGroup] 的配置,就是指定了这次写入时的钥匙串条目,不写入时默认为plist配置文件里第一个条目。

    在查询中,也可以对查询的钥匙串条目进行配置,默认会对所有有权限的条目进行搜索

     

    三、keychain的组成:

    参考博客:http://my.oschina.net/w11h22j33/blog/206713

    每一个keyChain的组成如图,整体是一个字典结构.
    1.kSecClass key 定义属于那一种类型的keyChain
    2.不同的类型包含不同的Attributes,这些attributes定义了这个item的具体信息
    3.每个item可以包含一个密码项来存储对应的密码

    对于最常用密码类型,我们应该如下配置

    [wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];//class

    [wrapper setObject:@"username" forKey:(id)kSecAttrAccount];//key

    [wrapper setObject:@"password"forKey:(id)kSecValueData];//value

    [wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];

    kSecAttrAccessiblein变量用来指定这个应用合适需要访问这个数据。我们需要对这个选项特别注意,并且使用最严格的选项。这个键(key)可以设置6种值。

    你可以参考以下:

    四、安全

    前面说到,APP能够访问的keychain数据是通过其entitlements文件指定的。

    但是!!如果使用带有一个*通配符的entitlments,因此它能够访问keychain中的所有条目。。或者,如果用一个包含所有访问组(access group)的entitlements文件,也能够访问所有的keychain数据。

    但以上仅限模拟器,在真机调试时,假如你plist里的条目,和自己调试文件(pro file)的ID不一致时(比如:EC0880A1.company),进行真机编译的时候是会报错的。

    所以真正要保证全家桶,还需要保证这些APP在同一个开发者ID下才行。 

    前面关于在未配置keychain-access-group的情况下,我参考别人说是默认存储在该BundleID的条目下,但实测并非存储在该条目下。具体为什么我也暂时不清楚。但是可以肯定的是,在iOS的沙盒机制中,你默认存储的条目下的K-V,其他APP是无法访问的(除了模拟器中的通配符*,真机是不行的)。

    关于Keychain还有很多值得挖掘的,比如具体在sqlite数据库的表单存储方式等,有错欢迎指正。

  • 相关阅读:
    Linux内核链表——看这一篇文章就够了
    2的幂和按位与&——效率
    fgets注意事项
    GDB TUI
    GDB TUI
    linux shell命令行选项与参数用法详解
    What are the differences between Perl, Python, AWK and sed
    What is the difference between sed and awk
    /proc/sysrq-trigger
    C++ Sqlite3的基本使用
  • 原文地址:https://www.cnblogs.com/rayshen/p/5072850.html
Copyright © 2011-2022 走看看