iOS 钥匙串keychain的应用

2021-11-10 10:16:09 浏览数 (1)

问题

目前需要保存一些用户配置,在app删掉后依然能正常读取,那么就用到【钥匙串】

钥匙串简介

项目中有时会需要存储敏感信息(如密码、密钥等),苹果官方提供了一种存储机制--钥匙串(keychain)。

keychain是一种存储在硬盘上的加密的数据库。这个可能是卸载App后,keychain信息还在的原因。

keychain适合存储 较小的数据量(不超过上千字节或上兆字节)的内容。

解决方案

JJKeychain.h

代码语言:txt复制
@interface JJKeychain : NSObject



//保存是先删掉之前的key,没有使用update,感觉这样简单;然后保存的value转换为NSData,如果value为自定义object,则需遵循NSSecureCoding协议

  (BOOL)setValue:(id)value forKey:(NSString *)key;



  (BOOL)setValue:(id)value forKey:(NSString *)key forAccessGroup:(nullable NSString *)group;





  (id)valueForKey:(NSString *)key;



  (id)valueForKey:(NSString *)key forAccessGroup:(nullable NSString *)group;



  (BOOL)deleteValueForKey:(NSString *)key;



  (BOOL)deleteValueForKey:(NSString *)key forAccessGroup:(nullable NSString *)group;



  (NSString *)getBundleSeedIdentifier;



@end

JJKeychain.m

代码语言:txt复制
@implementation JJKeychain



  (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;

}



  (NSString *)getFullAccessGroup:(NSString *)group

{

    NSString *accessGroup = nil;

    NSString *bundleSeedIdentifier = [self getBundleSeedIdentifier];

    if (bundleSeedIdentifier != nil && [group rangeOfString:bundleSeedIdentifier].location == NSNotFound) {

        accessGroup = [NSString stringWithFormat:@"%@.%@", bundleSeedIdentifier, group];

    }

    return accessGroup;

}



  (NSString *)getBundleSeedIdentifie

{

    static __strong NSString *bundleSeedIdentifier = nil;

    

    if (bundleSeedIdentifier == nil) {

        @synchronized(self) {

            if (bundleSeedIdentifier == nil) {

                NSString *_bundleSeedIdentifier = nil;

                NSDictionary *query = @{

                                        (__bridge id)kSecClass: (__bridge NSString *)kSecClassGenericPassword,

                                        (__bridge id)kSecAttrAccount: @"bundleSeedID",

                                        (__bridge id)kSecAttrService: @"",

                                        (__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue

                                        };

                CFDictionaryRef result = nil;

                OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);

                if (status == errSecItemNotFound) {

                    status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);

                }

                if (status == errSecSuccess) {

                    NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];

                    NSArray *components = [accessGroup componentsSeparatedByString:@"."];

//                    NSLog(@"components %@",components);

                    _bundleSeedIdentifier = [[components objectEnumerator] nextObject];

                    CFRelease(result);

                }

                if (_bundleSeedIdentifier != nil) {

                    bundleSeedIdentifier = [_bundleSeedIdentifier copy];

                }

            }

        }

    }

    

    return bundleSeedIdentifier;

}



  (BOOL)setValue:(id)value forKey:(NSString *)key{

    return [self setValue:value forKey:key forAccessGroup:nil];

}



  (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;

}



  (BOOL)deleteValueForKey:(NSString *)key{

    return [self deleteValueForKey:key forAccessGroup:nil];

}



  (BOOL)deleteValueForKey:(NSString *)key forAccessGroup:(NSString *)group{

    NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];

    OSStatus result = SecItemDelete((__bridge CFDictionaryRef)query);

    return result == errSecSuccess;

}



  (id)valueForKey:(NSString *)key{

    return [self valueForKey:key forAccessGroup:nil];

}



  (id)valueForKey:(NSString *)key forAccessGroup:(NSString *)group{

    id value = nil;

    NSMutableDictionary *query = [self getKeychainQuery:key forAccessGroup:group];

    CFDataRef keyData = NULL;

    [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];

    [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];

    if (SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&keyData) == errSecSuccess) {

        @try {

            value = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];

        }

        @catch (NSException *e) {

            NSLog(@"Unarchive of %@ failed: %@", key, e);

            value = nil;

        }

        

    }

    

    if (keyData) {

        CFRelease(keyData);

    }

    return value;

}



@end

参考:https://www.jianshu.com/p/354ea1279e68

0 人点赞