美文网首页iOS Developer上海快风信息科技有限公司iOS自我学习库
iOS 用keychain钥匙串保存账号、设备UUID及APP间

iOS 用keychain钥匙串保存账号、设备UUID及APP间

作者: 尛焱 | 来源:发表于2016-06-13 23:12 被阅读3631次

    iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失。

    1. 方法:增删查改
    OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)//增
    OSStatus SecItemDelete(CFDictionaryRef query) //删
    OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result) //查
    OSStatus SecItemUpdate(CFDictionaryRef query,
        CFDictionaryRef attributesToUpdate) //改
    

    所有方法的参数设置都需要通过CFDictionaryRef,所以创建一个专门获得NSDictionary的方法,然后通过桥接__bridge CFDictionaryRef的方式获得参数

    + (NSMutableDictionary *)getKeychainQuery:(NSString *)key forAccessGroup:(NSString *)group{
        NSMutableDictionary *query = @{(__bridge id)kSecClass                   : (__bridge id)kSecClassGenericPassword,
                                              (__bridge id)kSecAttrService      : key,
                                              (__bridge id)kSecAttrAccount      : key,
                                              (__bridge id)kSecAttrAccessible   : (__bridge id)kSecAttrAccessibleAfterFirstUnlock
                                              }.mutableCopy;
        if (group != nil) {
            [query setObject:[self getFullAccessGroup:group] forKey:(__bridge id)kSecAttrAccessGroup];
        }
        
        return query;
    }
    

    参数太多,而且在头文件SecItem.h中有详细说明,这里就不一一详说(详说也我也说不清),比如最后一个参数:

    kSecAttrAccessibleAfterFirstUnlock 
    Item data can only be accessed once the device has been unlocked after a restart.  
    This is recommended for items that need to be accesible by background applications.
    Items with this attribute will migrate to a new device when using encrypted backups.
    

    2.保存方法:

    + (BOOL)setValue:(id)value forKey:(NSString *)key forAccessGroup:(NSString *)group{
        NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];
        [self deleteValueForKey:key forAccessGroup:group];
        NSData *data = nil;
        @try {
            data = [NSKeyedArchiver archivedDataWithRootObject:value];
        } @catch (NSException *exception) {
            NSLog(@"archived failure value %@  %@",value,exception);
            return NO;
        }
        
        [query setObject:data forKey:(__bridge id)kSecValueData];
        OSStatus result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
        return result == errSecSuccess;
    }
    

    其中,保存是先删掉之前的key,没有使用update,感觉这样简单;然后保存的value转换为NSData,如果value为自定义object,则需遵循NSSecureCoding协议,实现编码方法,如下:

    @interface YTestObject : NSObject<NSSecureCoding>
    
    @property (strong,nonatomic) NSString *string;
    
    @end
    
    #import "YTestObject.h"
    
    @implementation YTestObject
    
    - (NSString *)description{
        return [NSString stringWithFormat:@"<YTestObject:string:%@>",_string];
    }
    
    + (BOOL)supportsSecureCoding{
        return YES;
    }
    
    - (void)encodeWithCoder:(NSCoder *)aCoder{
        [aCoder encodeObject:_string forKey:@"string"];
    }
    
    - (instancetype)initWithCoder:(NSCoder *)aDecoder{
        _string = [aDecoder decodeObjectForKey:@"string"];
        return self;
    }
    
    @end
    

    另外,为了防止在保存不支持NSSecureCoding协议的类时crash,使用了@try,简单介绍:

    @try {
            //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容
        } @catch (NSException *exception) {
            //除非try里面执行代码发生了异常,否则这里的代码不会执行
        } @finally {
            //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally
        }
    

    3.keychain-access-groups

    keychain允许同一个开发商的多个APP共享指定AccessGroup内的数据。
    A.创建plish文件如KeychainAccessGroups.plist,添加节点keykeychain-access-groupsarray,如下图:

    keychain-plist.png
    $(AppIdentifierPrefix)为开发者帐号对应的ID;

    B.在APP target的bulibSetting里面设置Code Signing Entitlements,指向包含AceessGroup的分组信息的plist文件,如下图(这时plist需在最顶层文件夹中):


    codesigning.png

    4.设备UUID
    有了上面的方法后,直接创建一个NSUUID保存即可;

    - (NSString *)getUUID{
        NSString *uuid = [YKeychain valueForKey:@"YDeviceUUID"];
        if (uuid == nil) {
            uuid = [NSUUID UUID].UUIDString;
            [YKeychain setValue:uuid forKey:@"YDeviceUUID"];
        }
        return uuid;
    }
    

    5.最后
    show me the code

    参考

    https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/doc/uid/TP30000898

    相关文章

      网友评论

        本文标题:iOS 用keychain钥匙串保存账号、设备UUID及APP间

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