KeychainItemWrapper ---- 官方封装文件

KeychainItemWrapper.H

/*    File: KeychainItemWrapper.h Abstract:  Objective-C wrapper for accessing a single keychain item.  Version: 1.2  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms.  If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software.  In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple.  Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated.  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  Copyright (C) 2010 Apple Inc. All Rights Reserved. 

*/

#import<UIKit/UIKit.h>

/*

The KeychainItemWrapper class is an abstraction layer for the iPhone Keychain communication. It is merely a

simple wrapper to provide a distinct barrier between all the idiosyncracies involved with the Keychain

CF/NS container objects.

*/

@interface KeychainItemWrapper : NSObject

{

NSMutableDictionary *keychainItemData; // The actual keychain item data backing store.

NSMutableDictionary *genericPasswordQuery; // A placeholder for the generic keychain item query used to locate the item.

}

@property (nonatomic, retain) NSMutableDictionary *keychainItemData;

@property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery;

// Designated initializer.

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;

- (void)setObject:(id)inObject forKey:(id)key;

- (id)objectForKey:(id)key;

// Initializes and resets the default generic keychain item data.

- (void)resetKeychainItem;

@end



keychainItemWrapper.m


/*    File: KeychainItemWrapper.m  Abstract:  Objective-C wrapper for accessing a single keychain item.    Version: 1.2    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple  Inc. ("Apple") in consideration of your agreement to the following  terms, and your use, installation, modification or redistribution of  this Apple software constitutes acceptance of these terms.  If you do  not agree with these terms, please do not use, install, modify or  redistribute this Apple software.    In consideration of your agreement to abide by the following terms, and  subject to these terms, Apple grants you a personal, non-exclusive  license, under Apple's copyrights in this original Apple software (the  "Apple Software"), to use, reproduce, modify and redistribute the Apple  Software, with or without modifications, in source and/or binary forms;  provided that if you redistribute the Apple Software in its entirety and  without modifications, you must retain this notice and the following  text and disclaimers in all such redistributions of the Apple Software.  Neither the name, trademarks, service marks or logos of Apple Inc. may  be used to endorse or promote products derived from the Apple Software  without specific prior written permission from Apple.  Except as  expressly stated in this notice, no other rights or licenses, express or  implied, are granted by Apple herein, including but not limited to any  patent rights that may be infringed by your derivative works or by other  works in which the Apple Software may be incorporated.    The Apple Software is provided by Apple on an "AS IS" basis.  APPLE  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.    IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE  POSSIBILITY OF SUCH DAMAGE.    Copyright (C) 2010 Apple Inc. All Rights Reserved.  */

 #import "KeychainItemWrapper.h"

#import<Security/Security.h>

/*

These are the default constants and their respective types,

available for the kSecClassGenericPassword Keychain Item class:

kSecAttrAccessGroup - CFStringRef

kSecAttrCreationDate - CFDateRef

kSecAttrModificationDate    - CFDateRef

kSecAttrDescription - CFStringRef

kSecAttrComment - CFStringRef

kSecAttrCreator - CFNumberRef

kSecAttrType                - CFNumberRef

kSecAttrLabel - CFStringRef

kSecAttrIsInvisible - CFBooleanRef

kSecAttrIsNegative - CFBooleanRef

kSecAttrAccount - CFStringRef

kSecAttrService - CFStringRef

kSecAttrGeneric - CFDataRef

See the header file Security/SecItem.h for more details.

*/

@interface KeychainItemWrapper (PrivateMethods)

/*

The decision behind the following two methods (secItemFormatToDictionary and dictionaryToSecItemFormat) was

to encapsulate the transition between what the detail view controller was expecting (NSString *) and what the

Keychain API expects as a validly constructed container class.

*/

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;

- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;

// Updates the item in the keychain, or adds it if it doesn't exist.

- (void)writeToKeychain;

@end

@implementation KeychainItemWrapper

@synthesize keychainItemData, genericPasswordQuery;

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup;

{

if (self = [super init])

{

// Begin Keychain search setup. The genericPasswordQuery leverages the special user

// defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain

// items which may be included by the same application.

genericPasswordQuery = [[NSMutableDictionary alloc] init];

[genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

[genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric];

// The keychain access group attribute determines if this item can be shared

// amongst multiple apps whose code signing entitlements contain the same keychain access group.

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

// Ignore the access group if running on the iPhone simulator.

//

// Apps that are built for the simulator aren't signed, so there's no keychain access group

// for the simulator to check. This means that all apps can see all keychain items when run

// on the simulator.

//

// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the

// simulator will return -25243 (errSecNoAccessForItem).

#else

[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

// Use the proper search constants, return only the attributes of the first match.

[genericPasswordQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];

[genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];

NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];

NSMutableDictionary *outDictionary = nil;

if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr)

{

// Stick these default values into keychain item if nothing found.

[self resetKeychainItem];

// Add the generic attribute and the keychain access group.

[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

// Ignore the access group if running on the iPhone simulator.

//

// Apps that are built for the simulator aren't signed, so there's no keychain access group

// for the simulator to check. This means that all apps can see all keychain items when run

// on the simulator.

//

// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the

// simulator will return -25243 (errSecNoAccessForItem).

#else

[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

}

else

{

// load the saved data from Keychain.

self.keychainItemData = [self secItemFormatToDictionary:outDictionary];

}

[outDictionary release];

}

return self;

}

- (void)dealloc

{

[keychainItemData release];

[genericPasswordQuery release];

[super dealloc];

}

- (void)setObject:(id)inObject forKey:(id)key

{

if (inObject == nil) return;

id currentObject = [keychainItemData objectForKey:key];

if (![currentObject isEqual:inObject])

{

[keychainItemData setObject:inObject forKey:key];

[self writeToKeychain];

}

}

- (id)objectForKey:(id)key

{

return [keychainItemData objectForKey:key];

}

- (void)resetKeychainItem

{

OSStatus junk = noErr;

if (!keychainItemData)

{

self.keychainItemData = [[NSMutableDictionary alloc] init];

}

else if (keychainItemData)

{

NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData];

junk = SecItemDelete((CFDictionaryRef)tempDictionary);

NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." );

}

// Default attributes for keychain item.

[keychainItemData setObject:@"" forKey:(id)kSecAttrAccount];

[keychainItemData setObject:@"" forKey:(id)kSecAttrLabel];

[keychainItemData setObject:@"" forKey:(id)kSecAttrDescription];

// Default data for keychain item.

[keychainItemData setObject:@"" forKey:(id)kSecValueData];

}

- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert

{

// The assumption is that this method will be called with a properly populated dictionary

// containing all the right key/value pairs for a SecItem.

// Create a dictionary to return populated with the attributes and data.

NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

// Add the Generic Password keychain item class attribute.

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

// Convert the NSString to NSData to meet the requirements for the value type kSecValueData.

// This is where to store sensitive data that should be encrypted.

NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];

[returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData];

return returnDictionary;

}

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert

{

// The assumption is that this method will be called with a properly populated dictionary

// containing all the right key/value pairs for the UI element.

// Create a dictionary to return populated with the attributes and data.

NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

// Add the proper search key and class attribute.

[returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

// Acquire the password data from the attributes.

NSData *passwordData = NULL;

if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr)

{

// Remove the search, class, and identifier key/value, we don't need them anymore.

[returnDictionary removeObjectForKey:(id)kSecReturnData];

// Add the password to the dictionary, converting from NSData to NSString.

NSString *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length]

encoding:NSUTF8StringEncoding] autorelease];

[returnDictionary setObject:password forKey:(id)kSecValueData];

}

else

{

// Don't do anything if nothing is found.

NSAssert(NO, @"Serious error, no matching item found in the keychain.\n");

}

[passwordData release];

return returnDictionary;

}

- (void)writeToKeychain

{

NSDictionary *attributes = NULL;

NSMutableDictionary *updateItem = NULL;

OSStatus result;

if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)

{

// First we need the attributes from the Keychain.

updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];

// Second we need to add the appropriate search key/values.

[updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];

// Lastly, we need to set up the updated attribute list being careful to remove the class.

NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];

[tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR

// Remove the access group if running on the iPhone simulator.

//

// Apps that are built for the simulator aren't signed, so there's no keychain access group

// for the simulator to check. This means that all apps can see all keychain items when run

// on the simulator.

//

// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the

// simulator will return -25243 (errSecNoAccessForItem).

//

// The access group attribute will be included in items returned by SecItemCopyMatching,

// which is why we need to remove it before updating the item.

[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];

#endif

// An implicit assumption is that you can only update a single item at a time.

result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);

NSAssert( result == noErr, @"Couldn't update the Keychain Item." );

}

else

{

// No previous item found; add the new one.

result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);

NSAssert( result == noErr, @"Couldn't add the Keychain Item." );

}

}

@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容