Files
UICKeyChainStore/UICKeyChainStore.m
kishikawakatsumi 183de45bce Migrate to ARC.
2013-04-25 01:59:41 +09:00

379 lines
13 KiB
Objective-C

//
// UICKeyChainStore.m
// UICKeyChainStore
//
// Created by Kishikawa Katsumi on 11/11/20.
// Copyright (c) 2011 Kishikawa Katsumi. All rights reserved.
//
#import "UICKeyChainStore.h"
static NSString *defaultService;
@interface UICKeyChainStore () {
NSMutableDictionary *itemsToUpdate;
}
@end
@implementation UICKeyChainStore
@synthesize service;
@synthesize accessGroup;
+ (void)initialize {
defaultService = [[NSBundle mainBundle] bundleIdentifier];
}
+ (NSString *)stringForKey:(NSString *)key {
return [self stringForKey:key service:defaultService accessGroup:nil];
}
+ (NSString *)stringForKey:(NSString *)key service:(NSString *)service {
return [self stringForKey:key service:service accessGroup:nil];
}
+ (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup {
NSData *data = [self dataForKey:key service:service accessGroup:accessGroup];
if (data) {
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
return nil;
}
+ (void)setString:(NSString *)value forKey:(NSString *)key {
[self setString:value forKey:key service:defaultService accessGroup:nil];
}
+ (void)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service {
[self setString:value forKey:key service:service accessGroup:nil];
}
+ (void)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup {
NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
[self setData:data forKey:key service:service accessGroup:accessGroup];
}
+ (NSData *)dataForKey:(NSString *)key {
return [self dataForKey:key service:defaultService accessGroup:nil];
}
+ (NSData *)dataForKey:(NSString *)key service:(NSString *)service {
return [self dataForKey:key service:service accessGroup:nil];
}
+ (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup {
if (!key) {
NSAssert(NO, @"key must not be nil.");
return nil;
}
if (!service) {
service = defaultService;
}
NSMutableDictionary* query = [NSMutableDictionary dictionary];
[query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[query setObject:service forKey:(__bridge id)kSecAttrService];
[query setObject:key forKey:(__bridge id)kSecAttrGeneric];
[query setObject:key forKey:(__bridge id)kSecAttrAccount];
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (accessGroup) {
[query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
CFTypeRef data = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &data);
if (status != errSecSuccess) {
return nil;
}
NSData *ret = [NSData dataWithData:(__bridge NSData *)data];
if (data) {
CFRelease(data);
}
return ret;
}
+ (void)setData:(NSData *)data forKey:(NSString *)key {
[self setData:data forKey:key service:defaultService accessGroup:nil];
}
+ (void)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service {
[self setData:data forKey:key service:service accessGroup:nil];
}
+ (void)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup {
if (!key) {
NSAssert(NO, @"key must not be nil.");
return;
}
if (!service) {
service = defaultService;
}
NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[query setObject:service forKey:(__bridge id)kSecAttrService];
[query setObject:key forKey:(__bridge id)kSecAttrGeneric];
[query setObject:key forKey:(__bridge id)kSecAttrAccount];
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (accessGroup) {
[query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
if (status == errSecSuccess) {
if (data) {
NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary];
[attributesToUpdate setObject:data forKey:(__bridge id)kSecValueData];
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate);
if (status != errSecSuccess) {
NSLog(@"%s|SecItemUpdate: error(%d)", __func__, status);
}
} else {
[self removeItemForKey:key service:service accessGroup:accessGroup];
}
} else if (status == errSecItemNotFound) {
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[attributes setObject:service forKey:(__bridge id)kSecAttrService];
[attributes setObject:key forKey:(__bridge id)kSecAttrGeneric];
[attributes setObject:key forKey:(__bridge id)kSecAttrAccount];
[attributes setObject:data forKey:(__bridge id)kSecValueData];
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (accessGroup) {
[attributes setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
if (status != errSecSuccess) {
NSLog(@"%s|SecItemAdd: error(%d)", __func__, status);
}
} else {
NSLog(@"%s|SecItemCopyMatching: error(%d)", __func__, status);
}
}
+ (void)removeItemForKey:(NSString *)key {
[UICKeyChainStore removeItemForKey:key service:defaultService accessGroup:nil];
}
+ (void)removeItemForKey:(NSString *)key service:(NSString *)service {
[UICKeyChainStore removeItemForKey:key service:service accessGroup:nil];
}
+ (void)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup {
if (!key) {
NSAssert(NO, @"key must not be nil.");
return;
}
if (!service) {
service = defaultService;
}
NSMutableDictionary *itemToDelete = [NSMutableDictionary dictionary];
[itemToDelete setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[itemToDelete setObject:service forKey:(__bridge id)kSecAttrService];
[itemToDelete setObject:key forKey:(__bridge id)kSecAttrGeneric];
[itemToDelete setObject:key forKey:(__bridge id)kSecAttrAccount];
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (accessGroup) {
[itemToDelete setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)itemToDelete);
if (status != errSecSuccess && status != errSecItemNotFound) {
NSLog(@"%s|SecItemDelete: error(%d)", __func__, status);
}
}
+ (NSArray *)itemsForService:(NSString *)service accessGroup:(NSString *)accessGroup {
if (!service) {
service = defaultService;
}
NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
[query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
[query setObject:service forKey:(__bridge id)kSecAttrService];
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (accessGroup) {
[query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
#endif
CFArrayRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecSuccess || status == errSecItemNotFound) {
return (__bridge NSArray*)result;
} else {
NSLog(@"%s|SecItemCopyMatching: error(%d)", __func__, status);
return nil;
}
}
+ (void)removeAllItems {
[self removeAllItemsForService:defaultService accessGroup:nil];
}
+ (void)removeAllItemsForService:(NSString *)service {
[self removeAllItemsForService:service accessGroup:nil];
}
+ (void)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup {
NSArray *items = [UICKeyChainStore itemsForService:service accessGroup:accessGroup];
for (NSDictionary *item in items) {
NSMutableDictionary *itemToDelete = [NSMutableDictionary dictionaryWithDictionary:item];
[itemToDelete setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)itemToDelete);
if (status != errSecSuccess) {
NSLog(@"%s|SecItemDelete: error(%d)", __func__, status);
NSLog(@"%@", itemToDelete);
}
}
}
#pragma mark -
+ (UICKeyChainStore *)keyChainStore {
return [[self alloc] initWithService:defaultService];
}
+ (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service {
return [[self alloc] initWithService:service];
}
+ (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service accessGroup:(NSString *)accessGroup {
return [[self alloc] initWithService:service accessGroup:accessGroup];
}
- (id)init {
return [self initWithService:defaultService accessGroup:nil];
}
- (id)initWithService:(NSString *)s {
return [self initWithService:s accessGroup:nil];
}
- (id)initWithService:(NSString *)s accessGroup:(NSString *)group {
self = [super init];
if (self) {
if (!s) {
s = defaultService;
}
service = [s copy];
accessGroup = [group copy];
if (accessGroup) {
#if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
[itemsToUpdate setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup];
#endif
}
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithDictionary:itemsToUpdate];
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
[query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
CFTypeRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (status == errSecSuccess) {
itemsToUpdate = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)result];
} else {
itemsToUpdate = [[NSMutableDictionary alloc] init];
}
}
return self;
}
#pragma mark -
- (NSString *)description {
NSArray *items = [UICKeyChainStore itemsForService:service accessGroup:accessGroup];
NSMutableArray *list = [NSMutableArray arrayWithCapacity:[items count]];
for (NSDictionary *attributes in items) {
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
[attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrService] forKey:@"Service"];
[attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrAccount] forKey:@"Account"];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
[attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrAccessGroup] forKey:@"AccessGroup"];
#endif
NSData *data = [attributes objectForKey:(__bridge id)kSecValueData];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (string) {
[attrs setObject:string forKey:@"Value"];
} else {
[attrs setObject:data forKey:@"Value"];
}
[list addObject:attrs];
}
return [list description];
}
#pragma mark -
- (void)setString:(NSString *)string forKey:(NSString *)key {
[self setData:[string dataUsingEncoding:NSUTF8StringEncoding] forKey:key];
}
- (NSString *)stringForKey:(id)key {
NSData *data = [self dataForKey:key];
if (data) {
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
return nil;
}
- (void)setData:(NSData *)data forKey:(NSString *)key {
if (!key) {
return;
}
if (!data) {
[self removeItemForKey:key];
} else {
[itemsToUpdate setObject:data forKey:key];
}
}
- (NSData *)dataForKey:(NSString *)key {
NSData *data = [itemsToUpdate objectForKey:key];
if (!data) {
data = [[self class] dataForKey:key service:service accessGroup:accessGroup];
}
return data;
}
- (void)removeItemForKey:(NSString *)key {
if ([itemsToUpdate objectForKey:key]) {
[itemsToUpdate removeObjectForKey:key];
} else {
[[self class] removeItemForKey:key service:service accessGroup:accessGroup];
}
}
#pragma mark -
- (void)removeAllItems {
[itemsToUpdate removeAllObjects];
[[self class] removeAllItemsForService:service accessGroup:accessGroup];
}
#pragma mark -
- (void)synchronize {
for (NSString *key in itemsToUpdate) {
[[self class] setData:[itemsToUpdate objectForKey:key] forKey:key service:service accessGroup:accessGroup];
}
}
@end