前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >iOS 钥匙串keychain的应用

iOS 钥匙串keychain的应用

原创
作者头像
freesan44
修改2021-11-10 10:16:09
修改2021-11-10 10:16:09
8380
举报
文章被收录于专栏:freesan44freesan44

问题

目前需要保存一些用户配置,在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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 钥匙串简介
  • 解决方案
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档