-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathIXKeychain.m
130 lines (96 loc) · 4.17 KB
/
IXKeychain.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//
// IXKeychain.m
// DishFreely
//
// Created by Luke Freeman on 10/6/11.
// Copyright (c) 2011 Luke Freeman. All rights reserved.
//
// Based on Keychain class from the OpenStack (Rackspace) project
// https://github.com/rackspace/rackspace-ios
//
#import "IXKeychain.h"
#import <Security/Security.h>
@interface Keychain ()
+ (NSString *)serviceName;
+ (NSMutableDictionary *)dictionaryBaseForKey:(NSString *)aKey;
+ (NSMutableDictionary *)dictionaryBaseForKey:(NSString *)aKey skipClass:(BOOL)aSkipClass;
@end
@implementation Keychain
static NSString *keychainServiceName = nil;
+ (NSString *)serviceName {
return (keychainServiceName == nil) ? @"service" : keychainServiceName;
}
+ (void)setServiceName:(NSString*)serviceName {
keychainServiceName = serviceName;
}
+ (NSMutableDictionary *)dictionaryBaseForKey:(NSString *)aKey {
return [Keychain dictionaryBaseForKey:aKey skipClass:NO];
}
+ (NSMutableDictionary *)dictionaryBaseForKey:(NSString *)aKey skipClass:(BOOL)aSkipClass {
NSMutableDictionary *searchDictionary = [NSMutableDictionary new];
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[searchDictionary setObject:[Keychain serviceName] forKey:(__bridge id)kSecAttrService];
[searchDictionary setObject:aKey forKey:(__bridge id)kSecAttrAccount];
return searchDictionary;
}
+ (OSStatus)valueStatusForKey:(NSString *)aKey {
NSDictionary *searchDictionary = [Keychain dictionaryBaseForKey:aKey];
return SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, nil);
}
+ (BOOL)hasValueForKey:(NSString *)aKey {
return ([Keychain valueStatusForKey:aKey] == errSecSuccess);
}
/*
* NOTE: aValue or any sub objects must conform to the NSCoding protocol
*/
+ (BOOL)setSecureValue:(id)aValue forKey:(NSString *)aKey {
if (aValue == nil || aKey == nil) return NO;
NSData *valueData = [NSKeyedArchiver archivedDataWithRootObject:aValue];
// check the status of the value (exists / doesn't)
OSStatus valStatus = [Keychain valueStatusForKey:aKey];
if (valStatus == errSecItemNotFound) {
// doesn't exist .. create
NSMutableDictionary *addQueryDict = [Keychain dictionaryBaseForKey:aKey];
[addQueryDict setObject:valueData forKey:(__bridge id)kSecValueData];
valStatus = SecItemAdd ((__bridge CFDictionaryRef)addQueryDict, NULL);
NSAssert1(valStatus == errSecSuccess, @"Value add returned status %d", (int)valStatus);
}
else if (valStatus == errSecSuccess) {
// exists .. update
NSMutableDictionary *updateQueryDict = [Keychain dictionaryBaseForKey:aKey];
NSDictionary *valueDict = [NSDictionary dictionaryWithObject:valueData forKey:(__bridge id)kSecValueData];
valStatus = SecItemUpdate ((__bridge CFDictionaryRef)updateQueryDict, (__bridge CFDictionaryRef)valueDict);
NSAssert1(valStatus == errSecSuccess, @"Value update returned status %d", (int)valStatus);
}
else {
NSAssert2(NO, @"Received mismatched status (%d) while setting keychain value for key %@", (int)valStatus, aKey);
}
return YES;
}
+ (id)secureValueForKey:(NSString *)aKey {
NSMutableDictionary *retrieveQueryDict = [Keychain dictionaryBaseForKey:aKey];
[retrieveQueryDict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; // make sure data comes back
CFDataRef dataRef = nil;
OSStatus queryResult = SecItemCopyMatching ((__bridge CFDictionaryRef)retrieveQueryDict, (CFTypeRef *)&dataRef);
if (queryResult == errSecSuccess) {
NSData *valueData = (__bridge_transfer NSData *)dataRef;
id value = [NSKeyedUnarchiver unarchiveObjectWithData:valueData];
return value;
}
else {
NSAssert2(queryResult == errSecItemNotFound, @"Received mismatched status (%d) while retriveing keychain value for key %@", (int)queryResult, aKey);
}
return nil;
}
+ (BOOL)removeSecureValueForKey:(NSString *)aKey {
NSDictionary *deleteQueryDict = [Keychain dictionaryBaseForKey:aKey];
OSStatus queryResult = SecItemDelete((__bridge CFDictionaryRef)deleteQueryDict);
if (queryResult == errSecSuccess) {
return YES;
}
else {
NSAssert2(queryResult == errSecItemNotFound, @"Received mismatched status (%d) while deleting keychain value for key %@", (int)queryResult, aKey);
return NO;
}
}
@end