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

iOS 钥匙串keychain的应用

作者头像
freesan44
发布2021-12-06 19:34:35
1.1K0
发布2021-12-06 19:34:35
举报
文章被收录于专栏:freesan44freesan44

问题

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

钥匙串简介

项目中有时会需要存储敏感信息(如密码、密钥等),苹果官方提供了一种存储机制--钥匙串(keychain)。 keychain是一种存储在硬盘上的加密的数据库。这个可能是卸载App后,keychain信息还在的原因。 keychain适合存储 较小的数据量(不超过上千字节或上兆字节)的内容。

解决方案

JJKeychain.h

代码语言:javascript
复制
@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

代码语言:javascript
复制
@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 *)getBundleSeedIdentifier
{
    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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/11/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 钥匙串简介
  • 解决方案
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档