Skip to content

Instantly share code, notes, and snippets.

@hide1202
Last active December 20, 2016 09:41
Show Gist options
  • Save hide1202/8cc61b4e837263fbde98151dd22344c5 to your computer and use it in GitHub Desktop.
Save hide1202/8cc61b4e837263fbde98151dd22344c5 to your computer and use it in GitHub Desktop.
iOS 키체인을 위한 헬퍼 클래스 입니다.
//
// KeyChain.h
// AppGroupProducer
//
// Created by VIEWPOINT on 2016. 12. 20..
// Copyright © 2016년 VIEWPOINT. All rights reserved.
//
#ifndef KeyChain_h
#define KeyChain_h
#import <Foundation/Foundation.h>
#import <Security/Security.h>
// Reference to http://hayageek.com/ios-keychain-tutorial/
@interface KeyChain : NSObject
+ (NSString *)groupIdWithAppIdPrefixWithKey:(NSString *)key;
- (id)initWithService:(NSString *)service_ withGroup:(NSString *)group_;
- (NSMutableDictionary *)prepareDict:(NSString *)key;
- (BOOL)insertWithKey:(NSString *)key :(NSData *)data;
- (NSData *)dataWithKey:(NSString *)key;
@end
@implementation KeyChain {
NSString *service;
NSString *group;
}
+ (NSString *)bundleSeedID {
NSDictionary *query = @{
(__bridge NSString *) kSecClass: (__bridge NSString *) kSecClassGenericPassword,
(__bridge NSString *) kSecAttrAccount: @"bundleSeedID",
(__bridge NSString *) kSecAttrService: @"",
(__bridge NSString *) kSecReturnAttributes: (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)
return nil;
NSString *accessGroup = ((__bridge NSDictionary *) result)[(__bridge NSString *) kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:@"."];
NSString *bundleSeedID = [[components objectEnumerator] nextObject];
CFRelease(result);
return bundleSeedID;
}
/**
* Return a group identifier appended with Application Identifier Prefix.
* @param key Identifier except for Application Identifier Prefix
* @return Group Identifier
*/
+ (NSString *)groupIdWithAppIdPrefixWithKey:(NSString *)key {
NSString *seedId = [KeyChain bundleSeedID];
NSString *result = [NSString stringWithFormat:@"%@.%@", seedId, key];
NSLog(@"SeedId : %@, GroupId : %@", seedId, result);
return result;
}
- (id)initWithService:(NSString *)service_ withGroup:(NSString *)group_ {
self = [super init];
if (self) {
service = [NSString stringWithString:service_];
if (group_)
group = [NSString stringWithString:group_];
}
return self;
}
- (NSMutableDictionary *)prepareDict:(NSString *)key {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[(__bridge id) kSecClass] = (__bridge id) kSecClassGenericPassword;
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
dict[(__bridge id) kSecAttrGeneric] = encodedKey;
dict[(__bridge id) kSecAttrAccount] = encodedKey;
dict[(__bridge id) kSecAttrService] = service;
dict[(__bridge id) kSecAttrAccessible] = (__bridge id) kSecAttrAccessibleAlwaysThisDeviceOnly;
//This is for sharing data across apps
if (group != nil)
dict[(__bridge id) kSecAttrAccessGroup] = group;
return dict;
}
- (BOOL)insertWithKey:(NSString *)key :(NSData *)data {
NSMutableDictionary *dict = [self prepareDict:key];
dict[(__bridge id) kSecValueData] = data;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
if (errSecSuccess != status) {
NSLog(@"Unable add item with key =%@ error:%d", key, (int) status);
}
return (errSecSuccess == status);
}
- (BOOL)update:(NSString *)key :(NSData *)data {
NSMutableDictionary *dictKey = [self prepareDict:key];
NSMutableDictionary *dictUpdate = [[NSMutableDictionary alloc] init];
dictUpdate[(__bridge id) kSecValueData] = data;
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef) dictKey, (__bridge CFDictionaryRef) dictUpdate);
if (errSecSuccess != status) {
NSLog(@"Unable add update with key =%@ error:%ld", key, status);
}
return (errSecSuccess == status);
return YES;
}
- (BOOL)remove:(NSString *)key {
NSMutableDictionary *dict = [self prepareDict:key];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef) dict);
if (status != errSecSuccess) {
NSLog(@"Unable to remove item for key %@ with error:%ld", key, status);
return NO;
}
return YES;
}
- (NSData *)dataWithKey:(NSString *)key {
NSMutableDictionary *dict = [self prepareDict:key];
dict[(__bridge id) kSecMatchLimit] = (__bridge id) kSecMatchLimitOne;
dict[(__bridge id) kSecReturnData] = (id) kCFBooleanTrue;
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) dict, &result);
if (status != errSecSuccess) {
NSLog(@"Unable to fetch item for key %@ with error:%d", key, (int) status);
return nil;
}
return (__bridge NSData *) result;
}
@end
#endif /* KeyChain_h */
//
// ViewController.m
// AppGroupProducer
//
// Created by VIEWPOINT on 2016. 12. 16..
// Copyright © 2016년 VIEWPOINT. All rights reserved.
//
#import "ViewController.h"
#import "KeyChain.h"
#define KEYCHAIN
//#define APP_GROUP
@interface ViewController ()
@property(weak, nonatomic) IBOutlet UITextField *textField;
@end
NSString *kDefaultSuiteName = @"group.viewpoint.test";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self load];
}
- (void)load {
#ifdef APP_GROUP
NSUserDefaults* sharedDefault = [[NSUserDefaults alloc] initWithSuiteName:kDefaultSuiteName];
NSString* text = [sharedDefault objectForKey:@"text"];
if(text)
self.textField.text = text;
#else
KeyChain *chain = [[KeyChain alloc] initWithService:@"Service"
withGroup:[KeyChain groupIdWithAppIdPrefixWithKey:@"com.viewpoint.AppGroupProducer"]];
NSData *data = [chain dataWithKey:@"text"];
if (data)
self.textField.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
#endif
}
- (IBAction)onClickSave:(id)sender {
#ifdef APP_GROUP
NSString* text = [self.textField text];
NSUserDefaults* sharedDefault = [[NSUserDefaults alloc] initWithSuiteName:kDefaultSuiteName];
[sharedDefault setObject:text forKey:@"text"];
[sharedDefault synchronize];
#else
KeyChain *chain = [[KeyChain alloc] initWithService:@"Service"
withGroup:[KeyChain groupIdWithAppIdPrefixWithKey:@"com.viewpoint.AppGroupProducer"]];
if ([chain dataWithKey:@"text"] == nil)
[chain insertWithKey:@"text" :[[self.textField text] dataUsingEncoding:NSUTF8StringEncoding]];
else
[chain update:@"text" :[[self.textField text] dataUsingEncoding:NSUTF8StringEncoding]];
#endif
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment