├── Assets ├── Main.cs ├── Main.unity └── Plugins │ ├── KeyChain.cs │ └── iOS │ ├── KeyChainPlugin.h │ ├── KeyChainPlugin.mm │ ├── UICKeyChainStore.h │ └── UICKeyChainStore.m ├── LICENSE └── README.md /Assets/Main.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class Main : MonoBehaviour { 5 | 6 | string currentUUID = ""; 7 | string saveUUID = ""; 8 | 9 | void Update () {} 10 | 11 | void OnGUI () { 12 | 13 | GUILayoutOption[] ops = new GUILayoutOption[]{}; 14 | 15 | GUI.skin.label.fontSize = 30; 16 | GUI.skin.textArea.fontSize = 30; 17 | GUI.skin.button.fontSize = 30; 18 | 19 | if (GUILayout.Button ("UUID", ops)) { 20 | currentUUID = SystemInfo.deviceUniqueIdentifier; 21 | Debug.Log ("CurrentUUID: [" + currentUUID + "]"); 22 | } 23 | 24 | GUILayout.TextArea (currentUUID, ops); 25 | 26 | if (GUILayout.Button ("Load UUID", ops)) { 27 | saveUUID = KeyChain.BindGetKeyChainUser (); 28 | Debug.Log ("LoadUUID: [" + saveUUID + "]"); 29 | } 30 | 31 | GUILayout.TextArea (saveUUID, ops); 32 | 33 | if (GUILayout.Button ("Save UUID", ops)) { 34 | currentUUID = SystemInfo.deviceUniqueIdentifier; 35 | KeyChain.BindSetKeyChainUser ("0", currentUUID); 36 | Debug.Log ("SaveUUID: [" + currentUUID + "]"); 37 | } 38 | 39 | if (GUILayout.Button ("Delete UUID", ops)) { 40 | KeyChain.BindDeleteKeyChainUser (); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Assets/Main.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamtanlong/unity-ios-keychain-plugin/cecb1189440a830016aab12be66ed975ecdac1bb/Assets/Main.unity -------------------------------------------------------------------------------- /Assets/Plugins/KeyChain.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Runtime.InteropServices; 3 | 4 | public class KeyChain { 5 | 6 | #if UNITY_IPHONE || UNITY_STANDALONE_OSX 7 | 8 | [DllImport("__Internal")] 9 | private static extern string getKeyChainUser(); 10 | 11 | public static string BindGetKeyChainUser() 12 | { 13 | return getKeyChainUser(); 14 | } 15 | 16 | [DllImport("__Internal")] 17 | private static extern void setKeyChainUser(string userId, string uuid); 18 | 19 | public static void BindSetKeyChainUser(string userId, string uuid) 20 | { 21 | setKeyChainUser(userId, uuid); 22 | } 23 | 24 | [DllImport("__Internal")] 25 | private static extern void deleteKeyChainUser(); 26 | 27 | public static void BindDeleteKeyChainUser() 28 | { 29 | deleteKeyChainUser(); 30 | } 31 | 32 | #endif 33 | } 34 | -------------------------------------------------------------------------------- /Assets/Plugins/iOS/KeyChainPlugin.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamtanlong/unity-ios-keychain-plugin/cecb1189440a830016aab12be66ed975ecdac1bb/Assets/Plugins/iOS/KeyChainPlugin.h -------------------------------------------------------------------------------- /Assets/Plugins/iOS/KeyChainPlugin.mm: -------------------------------------------------------------------------------- 1 | #import "KeyChainPlugin.h" 2 | #import "UICKeyChainStore.h" 3 |   4 | NSString *_keyForID = @"UserID"; 5 | NSString *_keyForUUID = @"UserUUID"; 6 |   7 | @implementation KeyChainPlugin 8 |   9 | extern "C" { 10 |     char* getKeyChainUser(); 11 |     void setKeyChainUser(const char* userId, const char* uuid); 12 |     void deleteKeyChainUser(); 13 | } 14 |   15 | char* getKeyChainUser() 16 | { 17 |     NSString *userId = [UICKeyChainStore stringForKey:_keyForID]; 18 |     NSString *userUUID = [UICKeyChainStore stringForKey:_keyForUUID]; 19 |   20 |     if (userId == nil || [userId isEqualToString:@""]) { 21 |         NSLog(@"No user information"); 22 |         userId = @""; 23 |         userUUID = @""; 24 |     } 25 |   26 |     NSString* json = [NSString stringWithFormat:@"{\"userId\":\"%@\",\"uuid\":\"%@\"}",userId,userUUID]; 27 |   28 |     return makeStringCopy([json UTF8String]); 29 | } 30 |   31 | void setKeyChainUser(const char* userId, const char* uuid) 32 | { 33 |     NSString *nsUseId = [NSString stringWithCString: userId encoding:NSUTF8StringEncoding]; 34 |     NSString *nsUUID = [NSString stringWithCString: uuid encoding:NSUTF8StringEncoding]; 35 |   36 |     [UICKeyChainStore setString:nsUseId forKey:_keyForID]; 37 |     [UICKeyChainStore setString:nsUUID forKey:_keyForUUID]; 38 | } 39 |   40 | void deleteKeyChainUser() 41 | { 42 |     [UICKeyChainStore removeItemForKey:_keyForID]; 43 |     [UICKeyChainStore removeItemForKey:_keyForUUID]; 44 | } 45 |   46 | char* makeStringCopy(const char* str) 47 | { 48 |     if (str == NULL) { 49 |         return NULL; 50 |     } 51 |   52 |     char* res = (char*)malloc(strlen(str) + 1); 53 |     strcpy(res, str); 54 |     return res; 55 | } 56 |   57 | @end -------------------------------------------------------------------------------- /Assets/Plugins/iOS/UICKeyChainStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICKeyChainStore.h 3 | // UICKeyChainStore 4 | // 5 | // Created by Kishikawa Katsumi on 11/11/20. 6 | // Copyright (c) 2011 Kishikawa Katsumi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #if !__has_feature(nullability) 12 | #define NS_ASSUME_NONNULL_BEGIN 13 | #define NS_ASSUME_NONNULL_END 14 | #define nullable 15 | #define nonnull 16 | #define null_unspecified 17 | #define null_resettable 18 | #define __nullable 19 | #define __nonnull 20 | #define __null_unspecified 21 | #endif 22 | 23 | #if __has_extension(objc_generics) 24 | #define UIC_KEY_TYPE 25 | #define UIC_CREDENTIAL_TYPE *> 26 | #else 27 | #define UIC_KEY_TYPE 28 | #define UIC_CREDENTIAL_TYPE 29 | #endif 30 | 31 | NS_ASSUME_NONNULL_BEGIN 32 | 33 | extern NSString * const UICKeyChainStoreErrorDomain; 34 | 35 | typedef NS_ENUM(NSInteger, UICKeyChainStoreErrorCode) { 36 | UICKeyChainStoreErrorInvalidArguments = 1, 37 | }; 38 | 39 | typedef NS_ENUM(NSInteger, UICKeyChainStoreItemClass) { 40 | UICKeyChainStoreItemClassGenericPassword = 1, 41 | UICKeyChainStoreItemClassInternetPassword, 42 | }; 43 | 44 | typedef NS_ENUM(NSInteger, UICKeyChainStoreProtocolType) { 45 | UICKeyChainStoreProtocolTypeFTP = 1, 46 | UICKeyChainStoreProtocolTypeFTPAccount, 47 | UICKeyChainStoreProtocolTypeHTTP, 48 | UICKeyChainStoreProtocolTypeIRC, 49 | UICKeyChainStoreProtocolTypeNNTP, 50 | UICKeyChainStoreProtocolTypePOP3, 51 | UICKeyChainStoreProtocolTypeSMTP, 52 | UICKeyChainStoreProtocolTypeSOCKS, 53 | UICKeyChainStoreProtocolTypeIMAP, 54 | UICKeyChainStoreProtocolTypeLDAP, 55 | UICKeyChainStoreProtocolTypeAppleTalk, 56 | UICKeyChainStoreProtocolTypeAFP, 57 | UICKeyChainStoreProtocolTypeTelnet, 58 | UICKeyChainStoreProtocolTypeSSH, 59 | UICKeyChainStoreProtocolTypeFTPS, 60 | UICKeyChainStoreProtocolTypeHTTPS, 61 | UICKeyChainStoreProtocolTypeHTTPProxy, 62 | UICKeyChainStoreProtocolTypeHTTPSProxy, 63 | UICKeyChainStoreProtocolTypeFTPProxy, 64 | UICKeyChainStoreProtocolTypeSMB, 65 | UICKeyChainStoreProtocolTypeRTSP, 66 | UICKeyChainStoreProtocolTypeRTSPProxy, 67 | UICKeyChainStoreProtocolTypeDAAP, 68 | UICKeyChainStoreProtocolTypeEPPC, 69 | UICKeyChainStoreProtocolTypeNNTPS, 70 | UICKeyChainStoreProtocolTypeLDAPS, 71 | UICKeyChainStoreProtocolTypeTelnetS, 72 | UICKeyChainStoreProtocolTypeIRCS, 73 | UICKeyChainStoreProtocolTypePOP3S, 74 | }; 75 | 76 | typedef NS_ENUM(NSInteger, UICKeyChainStoreAuthenticationType) { 77 | UICKeyChainStoreAuthenticationTypeNTLM = 1, 78 | UICKeyChainStoreAuthenticationTypeMSN, 79 | UICKeyChainStoreAuthenticationTypeDPA, 80 | UICKeyChainStoreAuthenticationTypeRPA, 81 | UICKeyChainStoreAuthenticationTypeHTTPBasic, 82 | UICKeyChainStoreAuthenticationTypeHTTPDigest, 83 | UICKeyChainStoreAuthenticationTypeHTMLForm, 84 | UICKeyChainStoreAuthenticationTypeDefault, 85 | }; 86 | 87 | typedef NS_ENUM(NSInteger, UICKeyChainStoreAccessibility) { 88 | UICKeyChainStoreAccessibilityWhenUnlocked = 1, 89 | UICKeyChainStoreAccessibilityAfterFirstUnlock, 90 | UICKeyChainStoreAccessibilityAlways, 91 | UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly 92 | __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0), 93 | UICKeyChainStoreAccessibilityWhenUnlockedThisDeviceOnly, 94 | UICKeyChainStoreAccessibilityAfterFirstUnlockThisDeviceOnly, 95 | UICKeyChainStoreAccessibilityAlwaysThisDeviceOnly, 96 | } 97 | __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_4_0); 98 | 99 | typedef NS_ENUM(NSInteger, UICKeyChainStoreAuthenticationPolicy) { 100 | UICKeyChainStoreAuthenticationPolicyUserPresence = kSecAccessControlUserPresence, 101 | }; 102 | 103 | @interface UICKeyChainStore : NSObject 104 | 105 | @property (nonatomic, readonly) UICKeyChainStoreItemClass itemClass; 106 | 107 | @property (nonatomic, readonly, nullable) NSString *service; 108 | @property (nonatomic, readonly, nullable) NSString *accessGroup; 109 | 110 | @property (nonatomic, readonly, nullable) NSURL *server; 111 | @property (nonatomic, readonly) UICKeyChainStoreProtocolType protocolType; 112 | @property (nonatomic, readonly) UICKeyChainStoreAuthenticationType authenticationType; 113 | 114 | @property (nonatomic) UICKeyChainStoreAccessibility accessibility; 115 | @property (nonatomic, readonly) UICKeyChainStoreAuthenticationPolicy authenticationPolicy 116 | __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); 117 | 118 | @property (nonatomic) BOOL synchronizable; 119 | 120 | @property (nonatomic, nullable) NSString *authenticationPrompt 121 | __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_8_0); 122 | 123 | @property (nonatomic, readonly, nullable) NSArray UIC_KEY_TYPE *allKeys; 124 | @property (nonatomic, readonly, nullable) NSArray *allItems; 125 | 126 | + (NSString *)defaultService; 127 | + (void)setDefaultService:(NSString *)defaultService; 128 | 129 | + (UICKeyChainStore *)keyChainStore; 130 | + (UICKeyChainStore *)keyChainStoreWithService:(nullable NSString *)service; 131 | + (UICKeyChainStore *)keyChainStoreWithService:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 132 | 133 | + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType; 134 | + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType; 135 | 136 | - (instancetype)init; 137 | - (instancetype)initWithService:(nullable NSString *)service; 138 | - (instancetype)initWithService:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 139 | 140 | - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType; 141 | - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType; 142 | 143 | + (nullable NSString *)stringForKey:(NSString *)key; 144 | + (nullable NSString *)stringForKey:(NSString *)key service:(nullable NSString *)service; 145 | + (nullable NSString *)stringForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 146 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key; 147 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service; 148 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 149 | 150 | + (nullable NSData *)dataForKey:(NSString *)key; 151 | + (nullable NSData *)dataForKey:(NSString *)key service:(nullable NSString *)service; 152 | + (nullable NSData *)dataForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 153 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key; 154 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service; 155 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 156 | 157 | - (BOOL)contains:(nullable NSString *)key; 158 | 159 | - (BOOL)setString:(nullable NSString *)string forKey:(nullable NSString *)key; 160 | - (BOOL)setString:(nullable NSString *)string forKey:(nullable NSString *)key label:(nullable NSString *)label comment:(nullable NSString *)comment; 161 | - (nullable NSString *)stringForKey:(NSString *)key; 162 | 163 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key; 164 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key label:(nullable NSString *)label comment:(nullable NSString *)comment; 165 | - (nullable NSData *)dataForKey:(NSString *)key; 166 | 167 | + (BOOL)removeItemForKey:(NSString *)key; 168 | + (BOOL)removeItemForKey:(NSString *)key service:(nullable NSString *)service; 169 | + (BOOL)removeItemForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 170 | 171 | + (BOOL)removeAllItems; 172 | + (BOOL)removeAllItemsForService:(nullable NSString *)service; 173 | + (BOOL)removeAllItemsForService:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup; 174 | 175 | - (BOOL)removeItemForKey:(NSString *)key; 176 | 177 | - (BOOL)removeAllItems; 178 | 179 | - (nullable NSString *)objectForKeyedSubscript:(NSString *)key; 180 | - (void)setObject:(nullable NSString *)obj forKeyedSubscript:(NSString *)key; 181 | 182 | + (nullable NSArray UIC_KEY_TYPE *)allKeysWithItemClass:(UICKeyChainStoreItemClass)itemClass; 183 | - (nullable NSArray UIC_KEY_TYPE *)allKeys; 184 | 185 | + (nullable NSArray *)allItemsWithItemClass:(UICKeyChainStoreItemClass)itemClass; 186 | - (nullable NSArray *)allItems; 187 | 188 | - (void)setAccessibility:(UICKeyChainStoreAccessibility)accessibility authenticationPolicy:(UICKeyChainStoreAuthenticationPolicy)authenticationPolicy 189 | __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); 190 | 191 | #if TARGET_OS_IOS 192 | - (void)sharedPasswordWithCompletion:(nullable void (^)(NSString * __nullable account, NSString * __nullable password, NSError * __nullable error))completion; 193 | - (void)sharedPasswordForAccount:(NSString *)account completion:(nullable void (^)(NSString * __nullable password, NSError * __nullable error))completion; 194 | 195 | - (void)setSharedPassword:(nullable NSString *)password forAccount:(NSString *)account completion:(nullable void (^)(NSError * __nullable error))completion; 196 | - (void)removeSharedPasswordForAccount:(NSString *)account completion:(nullable void (^)(NSError * __nullable error))completion; 197 | 198 | + (void)requestSharedWebCredentialWithCompletion:(nullable void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError * __nullable error))completion; 199 | + (void)requestSharedWebCredentialForDomain:(nullable NSString *)domain account:(nullable NSString *)account completion:(nullable void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError * __nullable error))completion; 200 | 201 | + (NSString *)generatePassword; 202 | #endif 203 | 204 | @end 205 | 206 | @interface UICKeyChainStore (ErrorHandling) 207 | 208 | + (nullable NSString *)stringForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 209 | + (nullable NSString *)stringForKey:(NSString *)key service:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 210 | + (nullable NSString *)stringForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 211 | 212 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 213 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 214 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 215 | 216 | + (nullable NSData *)dataForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 217 | + (nullable NSData *)dataForKey:(NSString *)key service:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 218 | + (nullable NSData *)dataForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 219 | 220 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 221 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 222 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 223 | 224 | - (BOOL)setString:(nullable NSString *)string forKey:(NSString * )key error:(NSError * __nullable __autoreleasing * __nullable)error; 225 | - (BOOL)setString:(nullable NSString *)string forKey:(NSString * )key label:(nullable NSString *)label comment:(nullable NSString *)comment error:(NSError * __nullable __autoreleasing * __nullable)error; 226 | 227 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 228 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key label:(nullable NSString *)label comment:(nullable NSString *)comment error:(NSError * __nullable __autoreleasing * __nullable)error; 229 | 230 | - (nullable NSString *)stringForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 231 | - (nullable NSData *)dataForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 232 | 233 | + (BOOL)removeItemForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 234 | + (BOOL)removeItemForKey:(NSString *)key service:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 235 | + (BOOL)removeItemForKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 236 | 237 | + (BOOL)removeAllItemsWithError:(NSError * __nullable __autoreleasing * __nullable)error; 238 | + (BOOL)removeAllItemsForService:(nullable NSString *)service error:(NSError * __nullable __autoreleasing * __nullable)error; 239 | + (BOOL)removeAllItemsForService:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup error:(NSError * __nullable __autoreleasing * __nullable)error; 240 | 241 | - (BOOL)removeItemForKey:(NSString *)key error:(NSError * __nullable __autoreleasing * __nullable)error; 242 | - (BOOL)removeAllItemsWithError:(NSError * __nullable __autoreleasing * __nullable)error; 243 | 244 | @end 245 | 246 | @interface UICKeyChainStore (ForwardCompatibility) 247 | 248 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute; 249 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 250 | 251 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service genericAttribute:(nullable id)genericAttribute; 252 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 253 | 254 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup genericAttribute:(nullable id)genericAttribute; 255 | + (BOOL)setString:(nullable NSString *)value forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 256 | 257 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute; 258 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 259 | 260 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service genericAttribute:(nullable id)genericAttribute; 261 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 262 | 263 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup genericAttribute:(nullable id)genericAttribute; 264 | + (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key service:(nullable NSString *)service accessGroup:(nullable NSString *)accessGroup genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 265 | 266 | - (BOOL)setString:(nullable NSString *)string forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute; 267 | - (BOOL)setString:(nullable NSString *)string forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 268 | 269 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute; 270 | - (BOOL)setData:(nullable NSData *)data forKey:(NSString *)key genericAttribute:(nullable id)genericAttribute error:(NSError * __nullable __autoreleasing * __nullable)error; 271 | 272 | @end 273 | 274 | @interface UICKeyChainStore (Deprecation) 275 | 276 | - (void)synchronize __attribute__((deprecated("calling this method is no longer required"))); 277 | - (BOOL)synchronizeWithError:(NSError * __nullable __autoreleasing * __nullable)error __attribute__((deprecated("calling this method is no longer required"))); 278 | 279 | @end 280 | 281 | NS_ASSUME_NONNULL_END 282 | -------------------------------------------------------------------------------- /Assets/Plugins/iOS/UICKeyChainStore.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICKeyChainStore.m 3 | // UICKeyChainStore 4 | // 5 | // Created by Kishikawa Katsumi on 11/11/20. 6 | // Copyright (c) 2011 Kishikawa Katsumi. All rights reserved. 7 | // 8 | 9 | #import "UICKeyChainStore.h" 10 | 11 | NSString * const UICKeyChainStoreErrorDomain = @"com.kishikawakatsumi.uickeychainstore"; 12 | static NSString *_defaultService; 13 | 14 | @interface UICKeyChainStore () 15 | 16 | @end 17 | 18 | @implementation UICKeyChainStore 19 | 20 | + (NSString *)defaultService 21 | { 22 | if (!_defaultService) { 23 | _defaultService = [[NSBundle mainBundle] bundleIdentifier] ?: @""; 24 | } 25 | 26 | return _defaultService; 27 | } 28 | 29 | + (void)setDefaultService:(NSString *)defaultService 30 | { 31 | _defaultService = defaultService; 32 | } 33 | 34 | #pragma mark - 35 | 36 | + (UICKeyChainStore *)keyChainStore 37 | { 38 | return [[self alloc] initWithService:nil accessGroup:nil]; 39 | } 40 | 41 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service 42 | { 43 | return [[self alloc] initWithService:service accessGroup:nil]; 44 | } 45 | 46 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service accessGroup:(NSString *)accessGroup 47 | { 48 | return [[self alloc] initWithService:service accessGroup:accessGroup]; 49 | } 50 | 51 | #pragma mark - 52 | 53 | + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType 54 | { 55 | return [[self alloc] initWithServer:server protocolType:protocolType authenticationType:UICKeyChainStoreAuthenticationTypeDefault]; 56 | } 57 | 58 | + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType 59 | { 60 | return [[self alloc] initWithServer:server protocolType:protocolType authenticationType:authenticationType]; 61 | } 62 | 63 | #pragma mark - 64 | 65 | - (instancetype)init 66 | { 67 | return [self initWithService:[self.class defaultService] accessGroup:nil]; 68 | } 69 | 70 | - (instancetype)initWithService:(NSString *)service 71 | { 72 | return [self initWithService:service accessGroup:nil]; 73 | } 74 | 75 | - (instancetype)initWithService:(NSString *)service accessGroup:(NSString *)accessGroup 76 | { 77 | self = [super init]; 78 | if (self) { 79 | _itemClass = UICKeyChainStoreItemClassGenericPassword; 80 | 81 | if (!service) { 82 | service = [self.class defaultService]; 83 | } 84 | _service = service.copy; 85 | _accessGroup = accessGroup.copy; 86 | [self commonInit]; 87 | } 88 | 89 | return self; 90 | } 91 | 92 | #pragma mark - 93 | 94 | - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType 95 | { 96 | return [self initWithServer:server protocolType:protocolType authenticationType:UICKeyChainStoreAuthenticationTypeDefault]; 97 | } 98 | 99 | - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType 100 | { 101 | self = [super init]; 102 | if (self) { 103 | _itemClass = UICKeyChainStoreItemClassInternetPassword; 104 | 105 | _server = server.copy; 106 | _protocolType = protocolType; 107 | _authenticationType = authenticationType; 108 | 109 | [self commonInit]; 110 | } 111 | 112 | return self; 113 | } 114 | 115 | #pragma mark - 116 | 117 | - (void)commonInit 118 | { 119 | _accessibility = UICKeyChainStoreAccessibilityAfterFirstUnlock; 120 | } 121 | 122 | #pragma mark - 123 | 124 | + (NSString *)stringForKey:(NSString *)key 125 | { 126 | return [self stringForKey:key service:nil accessGroup:nil error:nil]; 127 | } 128 | 129 | + (NSString *)stringForKey:(NSString *)key error:(NSError *__autoreleasing *)error 130 | { 131 | return [self stringForKey:key service:nil accessGroup:nil error:error]; 132 | } 133 | 134 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service 135 | { 136 | return [self stringForKey:key service:service accessGroup:nil error:nil]; 137 | } 138 | 139 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error 140 | { 141 | return [self stringForKey:key service:service accessGroup:nil error:error]; 142 | } 143 | 144 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 145 | { 146 | return [self stringForKey:key service:service accessGroup:accessGroup error:nil]; 147 | } 148 | 149 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 150 | { 151 | if (!key) { 152 | NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)]; 153 | if (error) { 154 | *error = e; 155 | } 156 | return nil; 157 | } 158 | if (!service) { 159 | service = [self defaultService]; 160 | } 161 | 162 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup]; 163 | return [keychain stringForKey:key error:error]; 164 | } 165 | 166 | #pragma mark - 167 | 168 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key 169 | { 170 | return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:nil error:nil]; 171 | } 172 | 173 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)error 174 | { 175 | return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:nil error:error]; 176 | } 177 | 178 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key genericAttribute:(id)genericAttribute 179 | { 180 | return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:nil]; 181 | } 182 | 183 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 184 | { 185 | return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:error]; 186 | } 187 | 188 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service 189 | { 190 | return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:nil error:nil]; 191 | } 192 | 193 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error 194 | { 195 | return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:nil error:error]; 196 | } 197 | 198 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute 199 | { 200 | return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:nil]; 201 | } 202 | 203 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 204 | { 205 | return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:error]; 206 | } 207 | 208 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 209 | { 210 | return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:nil]; 211 | } 212 | 213 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 214 | { 215 | return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:error]; 216 | } 217 | 218 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute 219 | { 220 | return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:nil]; 221 | } 222 | 223 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 224 | { 225 | if (!value) { 226 | return [self removeItemForKey:key service:service accessGroup:accessGroup error:error]; 227 | } 228 | NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding]; 229 | if (data) { 230 | return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:error]; 231 | } 232 | NSError *e = [self conversionError:NSLocalizedString(@"failed to convert string to data", nil)]; 233 | if (error) { 234 | *error = e; 235 | } 236 | return NO; 237 | } 238 | 239 | #pragma mark - 240 | 241 | + (NSData *)dataForKey:(NSString *)key 242 | { 243 | return [self dataForKey:key service:nil accessGroup:nil error:nil]; 244 | } 245 | 246 | + (NSData *)dataForKey:(NSString *)key error:(NSError *__autoreleasing *)error 247 | { 248 | return [self dataForKey:key service:nil accessGroup:nil error:error]; 249 | } 250 | 251 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service 252 | { 253 | return [self dataForKey:key service:service accessGroup:nil error:nil]; 254 | } 255 | 256 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error 257 | { 258 | return [self dataForKey:key service:service accessGroup:nil error:error]; 259 | } 260 | 261 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 262 | { 263 | return [self dataForKey:key service:service accessGroup:accessGroup error:nil]; 264 | } 265 | 266 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 267 | { 268 | if (!key) { 269 | NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)]; 270 | if (error) { 271 | *error = e; 272 | } 273 | return nil; 274 | } 275 | if (!service) { 276 | service = [self defaultService]; 277 | } 278 | 279 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup]; 280 | return [keychain dataForKey:key error:error]; 281 | } 282 | 283 | #pragma mark - 284 | 285 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key 286 | { 287 | return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:nil error:nil]; 288 | } 289 | 290 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError *__autoreleasing *)error 291 | { 292 | return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:nil error:error]; 293 | } 294 | 295 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute 296 | { 297 | return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:nil]; 298 | } 299 | 300 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 301 | { 302 | return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:error]; 303 | } 304 | 305 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service 306 | { 307 | return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:nil error:nil]; 308 | } 309 | 310 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error 311 | { 312 | return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:nil error:error]; 313 | } 314 | 315 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute 316 | { 317 | return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:nil]; 318 | } 319 | 320 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 321 | { 322 | return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:error]; 323 | } 324 | 325 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 326 | { 327 | return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:nil]; 328 | } 329 | 330 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 331 | { 332 | return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:error]; 333 | } 334 | 335 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute 336 | { 337 | return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:nil]; 338 | } 339 | 340 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 341 | { 342 | if (!key) { 343 | NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)]; 344 | if (error) { 345 | *error = e; 346 | } 347 | return NO; 348 | } 349 | if (!service) { 350 | service = [self defaultService]; 351 | } 352 | 353 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup]; 354 | return [keychain setData:data forKey:key genericAttribute:genericAttribute]; 355 | } 356 | 357 | #pragma mark - 358 | 359 | - (BOOL)contains:(NSString *)key 360 | { 361 | NSMutableDictionary *query = [self query]; 362 | query[(__bridge __strong id)kSecAttrAccount] = key; 363 | 364 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL); 365 | return status == errSecSuccess; 366 | } 367 | 368 | #pragma mark - 369 | 370 | - (NSString *)stringForKey:(id)key 371 | { 372 | return [self stringForKey:key error:nil]; 373 | } 374 | 375 | - (NSString *)stringForKey:(id)key error:(NSError *__autoreleasing *)error 376 | { 377 | NSData *data = [self dataForKey:key error:error]; 378 | if (data) { 379 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 380 | if (string) { 381 | return string; 382 | } 383 | NSError *e = [self.class conversionError:NSLocalizedString(@"failed to convert data to string", nil)]; 384 | if (error) { 385 | *error = e; 386 | } 387 | return nil; 388 | } 389 | 390 | return nil; 391 | } 392 | 393 | #pragma mark - 394 | 395 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key 396 | { 397 | return [self setString:string forKey:key genericAttribute:nil label:nil comment:nil error:nil]; 398 | } 399 | 400 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key error:(NSError *__autoreleasing *)error 401 | { 402 | return [self setString:string forKey:key genericAttribute:nil label:nil comment:nil error:error]; 403 | } 404 | 405 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute 406 | { 407 | return [self setString:string forKey:key genericAttribute:genericAttribute label:nil comment:nil error:nil]; 408 | } 409 | 410 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 411 | { 412 | return [self setString:string forKey:key genericAttribute:genericAttribute label:nil comment:nil error:error]; 413 | } 414 | 415 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment 416 | { 417 | return [self setString:string forKey:key genericAttribute:nil label:label comment:comment error:nil]; 418 | } 419 | 420 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error 421 | { 422 | return [self setString:string forKey:key genericAttribute:nil label:label comment:comment error:error]; 423 | } 424 | 425 | - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error 426 | { 427 | if (!string) { 428 | return [self removeItemForKey:key error:error]; 429 | } 430 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 431 | if (data) { 432 | return [self setData:data forKey:key genericAttribute:genericAttribute label:label comment:comment error:error]; 433 | } 434 | NSError *e = [self.class conversionError:NSLocalizedString(@"failed to convert string to data", nil)]; 435 | if (error) { 436 | *error = e; 437 | } 438 | return NO; 439 | } 440 | 441 | #pragma mark - 442 | 443 | - (NSData *)dataForKey:(NSString *)key 444 | { 445 | return [self dataForKey:key error:nil]; 446 | } 447 | 448 | - (NSData *)dataForKey:(NSString *)key error:(NSError *__autoreleasing *)error 449 | { 450 | NSMutableDictionary *query = [self query]; 451 | query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; 452 | query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue; 453 | 454 | query[(__bridge __strong id)kSecAttrAccount] = key; 455 | 456 | CFTypeRef data = nil; 457 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &data); 458 | 459 | if (status == errSecSuccess) { 460 | NSData *ret = [NSData dataWithData:(__bridge NSData *)data]; 461 | if (data) { 462 | CFRelease(data); 463 | return ret; 464 | } else { 465 | NSError *e = [self.class unexpectedError:NSLocalizedString(@"Unexpected error has occurred.", nil)]; 466 | if (error) { 467 | *error = e; 468 | } 469 | return nil; 470 | } 471 | } else if (status == errSecItemNotFound) { 472 | return nil; 473 | } 474 | 475 | NSError *e = [self.class securityError:status]; 476 | if (error) { 477 | *error = e; 478 | } 479 | return nil; 480 | } 481 | 482 | #pragma mark - 483 | 484 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key 485 | { 486 | return [self setData:data forKey:key genericAttribute:nil label:nil comment:nil error:nil]; 487 | } 488 | 489 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError *__autoreleasing *)error 490 | { 491 | return [self setData:data forKey:key genericAttribute:nil label:nil comment:nil error:error]; 492 | } 493 | 494 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute 495 | { 496 | return [self setData:data forKey:key genericAttribute:genericAttribute label:nil comment:nil error:nil]; 497 | } 498 | 499 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error 500 | { 501 | return [self setData:data forKey:key genericAttribute:genericAttribute label:nil comment:nil error:error]; 502 | } 503 | 504 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment 505 | { 506 | return [self setData:data forKey:key genericAttribute:nil label:label comment:comment error:nil]; 507 | } 508 | 509 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error 510 | { 511 | return [self setData:data forKey:key genericAttribute:nil label:label comment:comment error:error]; 512 | } 513 | 514 | - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error 515 | { 516 | if (!key) { 517 | NSError *e = [self.class argumentError:NSLocalizedString(@"the key must not to be nil", nil)]; 518 | if (error) { 519 | *error = e; 520 | } 521 | return NO; 522 | } 523 | if (!data) { 524 | return [self removeItemForKey:key error:error]; 525 | } 526 | 527 | NSMutableDictionary *query = [self query]; 528 | query[(__bridge __strong id)kSecAttrAccount] = key; 529 | #if TARGET_OS_IOS 530 | if (floor(NSFoundationVersionNumber) > floor(1144.17)) { // iOS 9+ 531 | query[(__bridge __strong id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; 532 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0 533 | } else if (floor(NSFoundationVersionNumber) > floor(1047.25)) { // iOS 8+ 534 | query[(__bridge __strong id)kSecUseNoAuthenticationUI] = (__bridge id)kCFBooleanTrue; 535 | #endif 536 | } 537 | #elif TARGET_OS_WATCH || TARGET_OS_TV 538 | query[(__bridge __strong id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail; 539 | #endif 540 | 541 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL); 542 | if (status == errSecSuccess || status == errSecInteractionNotAllowed) { 543 | query = [self query]; 544 | query[(__bridge __strong id)kSecAttrAccount] = key; 545 | 546 | NSError *unexpectedError = nil; 547 | NSMutableDictionary *attributes = [self attributesWithKey:nil value:data error:&unexpectedError]; 548 | 549 | if (genericAttribute) { 550 | attributes[(__bridge __strong id)kSecAttrGeneric] = genericAttribute; 551 | } 552 | if (label) { 553 | attributes[(__bridge __strong id)kSecAttrLabel] = label; 554 | } 555 | if (comment) { 556 | attributes[(__bridge __strong id)kSecAttrComment] = comment; 557 | } 558 | 559 | if (unexpectedError) { 560 | NSLog(@"error: [%@] %@", @(unexpectedError.code), NSLocalizedString(@"Unexpected error has occurred.", nil)); 561 | if (error) { 562 | *error = unexpectedError; 563 | } 564 | return NO; 565 | } else { 566 | 567 | if (status == errSecInteractionNotAllowed && floor(NSFoundationVersionNumber) <= floor(1140.11)) { // iOS 8.0.x 568 | if ([self removeItemForKey:key error:error]) { 569 | return [self setData:data forKey:key label:label comment:comment error:error]; 570 | } 571 | } else { 572 | status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes); 573 | } 574 | if (status != errSecSuccess) { 575 | NSError *e = [self.class securityError:status]; 576 | if (error) { 577 | *error = e; 578 | } 579 | return NO; 580 | } 581 | } 582 | } else if (status == errSecItemNotFound) { 583 | NSError *unexpectedError = nil; 584 | NSMutableDictionary *attributes = [self attributesWithKey:key value:data error:&unexpectedError]; 585 | 586 | if (genericAttribute) { 587 | attributes[(__bridge __strong id)kSecAttrGeneric] = genericAttribute; 588 | } 589 | if (label) { 590 | attributes[(__bridge __strong id)kSecAttrLabel] = label; 591 | } 592 | if (comment) { 593 | attributes[(__bridge __strong id)kSecAttrComment] = comment; 594 | } 595 | 596 | if (unexpectedError) { 597 | NSLog(@"error: [%@] %@", @(unexpectedError.code), NSLocalizedString(@"Unexpected error has occurred.", nil)); 598 | if (error) { 599 | *error = unexpectedError; 600 | } 601 | return NO; 602 | } else { 603 | status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL); 604 | if (status != errSecSuccess) { 605 | NSError *e = [self.class securityError:status]; 606 | if (error) { 607 | *error = e; 608 | } 609 | return NO; 610 | } 611 | } 612 | } else { 613 | NSError *e = [self.class securityError:status]; 614 | if (error) { 615 | *error = e; 616 | } 617 | return NO; 618 | } 619 | 620 | return YES; 621 | } 622 | 623 | #pragma mark - 624 | 625 | + (BOOL)removeItemForKey:(NSString *)key 626 | { 627 | return [self removeItemForKey:key service:nil accessGroup:nil error:nil]; 628 | } 629 | 630 | + (BOOL)removeItemForKey:(NSString *)key error:(NSError *__autoreleasing *)error 631 | { 632 | return [self removeItemForKey:key service:nil accessGroup:nil error:error]; 633 | } 634 | 635 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service 636 | { 637 | return [self removeItemForKey:key service:service accessGroup:nil error:nil]; 638 | } 639 | 640 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error 641 | { 642 | return [self removeItemForKey:key service:service accessGroup:nil error:error]; 643 | } 644 | 645 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 646 | { 647 | return [self removeItemForKey:key service:service accessGroup:accessGroup error:nil]; 648 | } 649 | 650 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 651 | { 652 | if (!key) { 653 | NSError *e = [self.class argumentError:NSLocalizedString(@"the key must not to be nil", nil)]; 654 | if (error) { 655 | *error = e; 656 | } 657 | return NO; 658 | } 659 | if (!service) { 660 | service = [self defaultService]; 661 | } 662 | 663 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup]; 664 | return [keychain removeItemForKey:key error:error]; 665 | } 666 | 667 | #pragma mark - 668 | 669 | + (BOOL)removeAllItems 670 | { 671 | return [self removeAllItemsForService:nil accessGroup:nil error:nil]; 672 | } 673 | 674 | + (BOOL)removeAllItemsWithError:(NSError *__autoreleasing *)error 675 | { 676 | return [self removeAllItemsForService:nil accessGroup:nil error:error]; 677 | } 678 | 679 | + (BOOL)removeAllItemsForService:(NSString *)service 680 | { 681 | return [self removeAllItemsForService:service accessGroup:nil error:nil]; 682 | } 683 | 684 | + (BOOL)removeAllItemsForService:(NSString *)service error:(NSError *__autoreleasing *)error 685 | { 686 | return [self removeAllItemsForService:service accessGroup:nil error:error]; 687 | } 688 | 689 | + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup 690 | { 691 | return [self removeAllItemsForService:service accessGroup:accessGroup error:nil]; 692 | } 693 | 694 | + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error 695 | { 696 | UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup]; 697 | return [keychain removeAllItemsWithError:error]; 698 | } 699 | 700 | #pragma mark - 701 | 702 | - (BOOL)removeItemForKey:(NSString *)key 703 | { 704 | return [self removeItemForKey:key error:nil]; 705 | } 706 | 707 | - (BOOL)removeItemForKey:(NSString *)key error:(NSError *__autoreleasing *)error 708 | { 709 | NSMutableDictionary *query = [self query]; 710 | query[(__bridge __strong id)kSecAttrAccount] = key; 711 | 712 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); 713 | if (status != errSecSuccess && status != errSecItemNotFound) { 714 | NSError *e = [self.class securityError:status]; 715 | if (error) { 716 | *error = e; 717 | } 718 | return NO; 719 | } 720 | 721 | return YES; 722 | } 723 | 724 | #pragma mark - 725 | 726 | - (BOOL)removeAllItems 727 | { 728 | return [self removeAllItemsWithError:nil]; 729 | } 730 | 731 | - (BOOL)removeAllItemsWithError:(NSError *__autoreleasing *)error 732 | { 733 | NSMutableDictionary *query = [self query]; 734 | #if !TARGET_OS_IPHONE 735 | query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; 736 | #endif 737 | 738 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); 739 | if (status != errSecSuccess && status != errSecItemNotFound) { 740 | NSError *e = [self.class securityError:status]; 741 | if (error) { 742 | *error = e; 743 | } 744 | return NO; 745 | } 746 | 747 | return YES; 748 | } 749 | 750 | #pragma mark - 751 | 752 | - (NSString *)objectForKeyedSubscript:(NSString *)key 753 | { 754 | return [self stringForKey:key]; 755 | } 756 | 757 | - (void)setObject:(NSString *)obj forKeyedSubscript:(NSString *)key 758 | { 759 | if (!obj) { 760 | [self removeItemForKey:key]; 761 | } else { 762 | [self setString:obj forKey:key]; 763 | } 764 | } 765 | 766 | #pragma mark - 767 | 768 | - (NSArray UIC_KEY_TYPE *)allKeys 769 | { 770 | NSArray *items = [self.class prettify:[self itemClassObject] items:[self items]]; 771 | NSMutableArray *keys = [[NSMutableArray alloc] init]; 772 | for (NSDictionary *item in items) { 773 | NSString *key = item[@"key"]; 774 | if (key) { 775 | [keys addObject:key]; 776 | } 777 | } 778 | return keys.copy; 779 | } 780 | 781 | + (NSArray UIC_KEY_TYPE *)allKeysWithItemClass:(UICKeyChainStoreItemClass)itemClass 782 | { 783 | CFTypeRef itemClassObject = kSecClassGenericPassword; 784 | if (itemClass == UICKeyChainStoreItemClassGenericPassword) { 785 | itemClassObject = kSecClassGenericPassword; 786 | } else if (itemClass == UICKeyChainStoreItemClassInternetPassword) { 787 | itemClassObject = kSecClassInternetPassword; 788 | } 789 | 790 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 791 | query[(__bridge __strong id)kSecClass] = (__bridge id)itemClassObject; 792 | query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; 793 | query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; 794 | 795 | CFArrayRef result = nil; 796 | CFDictionaryRef cfquery = (CFDictionaryRef)CFBridgingRetain(query); 797 | OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&result); 798 | CFRelease(cfquery); 799 | 800 | if (status == errSecSuccess) { 801 | NSArray *items = [self prettify:itemClassObject items:(__bridge NSArray *)result]; 802 | NSMutableArray *keys = [[NSMutableArray alloc] init]; 803 | for (NSDictionary *item in items) { 804 | if (itemClassObject == kSecClassGenericPassword) { 805 | [keys addObject:@{@"service": item[@"service"] ?: @"", @"key": item[@"key"] ?: @""}]; 806 | } else if (itemClassObject == kSecClassInternetPassword) { 807 | [keys addObject:@{@"server": item[@"service"] ?: @"", @"key": item[@"key"] ?: @""}]; 808 | } 809 | } 810 | return keys.copy; 811 | } else if (status == errSecItemNotFound) { 812 | return @[]; 813 | } 814 | 815 | return nil; 816 | } 817 | 818 | + (NSArray *)allItemsWithItemClass:(UICKeyChainStoreItemClass)itemClass 819 | { 820 | CFTypeRef itemClassObject = kSecClassGenericPassword; 821 | if (itemClass == UICKeyChainStoreItemClassGenericPassword) { 822 | itemClassObject = kSecClassGenericPassword; 823 | } else if (itemClass == UICKeyChainStoreItemClassInternetPassword) { 824 | itemClassObject = kSecClassInternetPassword; 825 | } 826 | 827 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 828 | query[(__bridge __strong id)kSecClass] = (__bridge id)itemClassObject; 829 | query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; 830 | query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; 831 | #if TARGET_OS_IPHONE 832 | query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue; 833 | #endif 834 | 835 | CFArrayRef result = nil; 836 | CFDictionaryRef cfquery = (CFDictionaryRef)CFBridgingRetain(query); 837 | OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&result); 838 | CFRelease(cfquery); 839 | 840 | if (status == errSecSuccess) { 841 | return [self prettify:itemClassObject items:(__bridge NSArray *)result]; 842 | } else if (status == errSecItemNotFound) { 843 | return @[]; 844 | } 845 | 846 | return nil; 847 | } 848 | 849 | - (NSArray *)allItems 850 | { 851 | return [self.class prettify:[self itemClassObject] items:[self items]]; 852 | } 853 | 854 | - (NSArray *)items 855 | { 856 | NSMutableDictionary *query = [self query]; 857 | query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; 858 | query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; 859 | #if TARGET_OS_IPHONE 860 | query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue; 861 | #endif 862 | 863 | CFArrayRef result = nil; 864 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query,(CFTypeRef *)&result); 865 | 866 | if (status == errSecSuccess) { 867 | return CFBridgingRelease(result); 868 | } else if (status == errSecItemNotFound) { 869 | return @[]; 870 | } 871 | 872 | return nil; 873 | } 874 | 875 | + (NSArray *)prettify:(CFTypeRef)itemClass items:(NSArray *)items 876 | { 877 | NSMutableArray *prettified = [[NSMutableArray alloc] init]; 878 | 879 | for (NSDictionary *attributes in items) { 880 | NSMutableDictionary *item = [[NSMutableDictionary alloc] init]; 881 | if (itemClass == kSecClassGenericPassword) { 882 | item[@"class"] = @"GenericPassword"; 883 | id service = attributes[(__bridge id)kSecAttrService]; 884 | if (service) { 885 | item[@"service"] = service; 886 | } 887 | id accessGroup = attributes[(__bridge id)kSecAttrAccessGroup]; 888 | if (accessGroup) { 889 | item[@"accessGroup"] = accessGroup; 890 | } 891 | } else if (itemClass == kSecClassInternetPassword) { 892 | item[@"class"] = @"InternetPassword"; 893 | id server = attributes[(__bridge id)kSecAttrServer]; 894 | if (server) { 895 | item[@"server"] = server; 896 | } 897 | id protocolType = attributes[(__bridge id)kSecAttrProtocol]; 898 | if (protocolType) { 899 | item[@"protocol"] = protocolType; 900 | } 901 | id authenticationType = attributes[(__bridge id)kSecAttrAuthenticationType]; 902 | if (authenticationType) { 903 | item[@"authenticationType"] = authenticationType; 904 | } 905 | } 906 | id key = attributes[(__bridge id)kSecAttrAccount]; 907 | if (key) { 908 | item[@"key"] = key; 909 | } 910 | NSData *data = attributes[(__bridge id)kSecValueData]; 911 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 912 | if (string) { 913 | item[@"value"] = string; 914 | } else { 915 | item[@"value"] = data; 916 | } 917 | 918 | id accessible = attributes[(__bridge id)kSecAttrAccessible]; 919 | if (accessible) { 920 | item[@"accessibility"] = accessible; 921 | } 922 | 923 | if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+ 924 | id synchronizable = attributes[(__bridge id)kSecAttrSynchronizable]; 925 | if (synchronizable) { 926 | item[@"synchronizable"] = synchronizable; 927 | } 928 | } 929 | 930 | [prettified addObject:item]; 931 | } 932 | 933 | return prettified.copy; 934 | } 935 | 936 | #pragma mark - 937 | 938 | - (void)setSynchronizable:(BOOL)synchronizable 939 | { 940 | _synchronizable = synchronizable; 941 | if (_authenticationPolicy) { 942 | NSLog(@"%@", @"Cannot specify both an authenticationPolicy and a synchronizable"); 943 | } 944 | } 945 | 946 | - (void)setAccessibility:(UICKeyChainStoreAccessibility)accessibility authenticationPolicy:(UICKeyChainStoreAuthenticationPolicy)authenticationPolicy 947 | { 948 | _accessibility = accessibility; 949 | _authenticationPolicy = authenticationPolicy; 950 | if (_synchronizable) { 951 | NSLog(@"%@", @"Cannot specify both an authenticationPolicy and a synchronizable"); 952 | } 953 | } 954 | 955 | #pragma mark - 956 | 957 | #if TARGET_OS_IOS 958 | - (void)sharedPasswordWithCompletion:(void (^)(NSString *account, NSString *password, NSError *error))completion 959 | { 960 | NSString *domain = self.server.host; 961 | if (domain.length > 0) { 962 | [self.class requestSharedWebCredentialForDomain:domain account:nil completion:^(NSArray *credentials, NSError *error) { 963 | NSDictionary *credential = credentials.firstObject; 964 | if (credential) { 965 | NSString *account = credential[@"account"]; 966 | NSString *password = credential[@"password"]; 967 | if (completion) { 968 | completion(account, password, error); 969 | } 970 | } else { 971 | if (completion) { 972 | completion(nil, nil, error); 973 | } 974 | } 975 | }]; 976 | } else { 977 | NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)]; 978 | if (completion) { 979 | completion(nil, nil, error); 980 | } 981 | } 982 | } 983 | 984 | - (void)sharedPasswordForAccount:(NSString *)account completion:(void (^)(NSString *password, NSError *error))completion 985 | { 986 | NSString *domain = self.server.host; 987 | if (domain.length > 0) { 988 | [self.class requestSharedWebCredentialForDomain:domain account:account completion:^(NSArray *credentials, NSError *error) { 989 | NSDictionary *credential = credentials.firstObject; 990 | if (credential) { 991 | NSString *password = credential[@"password"]; 992 | if (completion) { 993 | completion(password, error); 994 | } 995 | } else { 996 | if (completion) { 997 | completion(nil, error); 998 | } 999 | } 1000 | }]; 1001 | } else { 1002 | NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)]; 1003 | if (completion) { 1004 | completion(nil, error); 1005 | } 1006 | } 1007 | } 1008 | 1009 | - (void)setSharedPassword:(NSString *)password forAccount:(NSString *)account completion:(void (^)(NSError *error))completion 1010 | { 1011 | NSString *domain = self.server.host; 1012 | if (domain.length > 0) { 1013 | SecAddSharedWebCredential((__bridge CFStringRef)domain, (__bridge CFStringRef)account, (__bridge CFStringRef)password, ^(CFErrorRef error) { 1014 | if (completion) { 1015 | completion((__bridge NSError *)error); 1016 | } 1017 | }); 1018 | } else { 1019 | NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)]; 1020 | if (completion) { 1021 | completion(error); 1022 | } 1023 | } 1024 | } 1025 | 1026 | - (void)removeSharedPasswordForAccount:(NSString *)account completion:(void (^)(NSError *error))completion 1027 | { 1028 | [self setSharedPassword:nil forAccount:account completion:completion]; 1029 | } 1030 | 1031 | + (void)requestSharedWebCredentialWithCompletion:(void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError *error))completion 1032 | { 1033 | [self requestSharedWebCredentialForDomain:nil account:nil completion:completion]; 1034 | } 1035 | 1036 | + (void)requestSharedWebCredentialForDomain:(NSString *)domain account:(NSString *)account completion:(void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError *error))completion 1037 | { 1038 | SecRequestSharedWebCredential((__bridge CFStringRef)domain, (__bridge CFStringRef)account, ^(CFArrayRef credentials, CFErrorRef error) { 1039 | if (error) { 1040 | NSError *e = (__bridge NSError *)error; 1041 | if (e.code != errSecItemNotFound) { 1042 | NSLog(@"error: [%@] %@", @(e.code), e.localizedDescription); 1043 | } 1044 | } 1045 | 1046 | NSMutableArray *sharedCredentials = [[NSMutableArray alloc] init]; 1047 | for (NSDictionary *credential in (__bridge NSArray *)credentials) { 1048 | NSMutableDictionary *sharedCredential = [[NSMutableDictionary alloc] init]; 1049 | NSString *server = credential[(__bridge __strong id)kSecAttrServer]; 1050 | if (server) { 1051 | sharedCredential[@"server"] = server; 1052 | } 1053 | NSString *account = credential[(__bridge __strong id)kSecAttrAccount]; 1054 | if (account) { 1055 | sharedCredential[@"account"] = account; 1056 | } 1057 | NSString *password = credential[(__bridge __strong id)kSecSharedPassword]; 1058 | if (password) { 1059 | sharedCredential[@"password"] = password; 1060 | } 1061 | [sharedCredentials addObject:sharedCredential]; 1062 | } 1063 | 1064 | if (completion) { 1065 | completion(sharedCredentials.copy, (__bridge NSError *)error); 1066 | } 1067 | }); 1068 | } 1069 | 1070 | + (NSString *)generatePassword 1071 | { 1072 | return (NSString *)CFBridgingRelease(SecCreateSharedWebCredentialPassword()); 1073 | } 1074 | 1075 | #endif 1076 | 1077 | #pragma mark - 1078 | 1079 | - (NSString *)description 1080 | { 1081 | NSArray *items = [self allItems]; 1082 | if (items.count == 0) { 1083 | return @"()"; 1084 | } 1085 | NSMutableString *description = [[NSMutableString alloc] initWithString:@"(\n"]; 1086 | for (NSDictionary *item in items) { 1087 | [description appendFormat:@" %@", item]; 1088 | } 1089 | [description appendString:@")"]; 1090 | return description.copy; 1091 | } 1092 | 1093 | - (NSString *)debugDescription 1094 | { 1095 | return [NSString stringWithFormat:@"%@", [self items]]; 1096 | } 1097 | 1098 | #pragma mark - 1099 | 1100 | - (NSMutableDictionary *)query 1101 | { 1102 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 1103 | 1104 | CFTypeRef itemClass = [self itemClassObject]; 1105 | query[(__bridge __strong id)kSecClass] =(__bridge id)itemClass; 1106 | if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+ (NSFoundationVersionNumber_iOS_6_1) 1107 | query[(__bridge __strong id)kSecAttrSynchronizable] = (__bridge id)kSecAttrSynchronizableAny; 1108 | } 1109 | 1110 | if (itemClass == kSecClassGenericPassword) { 1111 | query[(__bridge __strong id)(kSecAttrService)] = _service; 1112 | #if !TARGET_OS_SIMULATOR 1113 | if (_accessGroup) { 1114 | query[(__bridge __strong id)kSecAttrAccessGroup] = _accessGroup; 1115 | } 1116 | #endif 1117 | } else { 1118 | if (_server.host) { 1119 | query[(__bridge __strong id)kSecAttrServer] = _server.host; 1120 | } 1121 | if (_server.port) { 1122 | query[(__bridge __strong id)kSecAttrPort] = _server.port; 1123 | } 1124 | CFTypeRef protocolTypeObject = [self protocolTypeObject]; 1125 | if (protocolTypeObject) { 1126 | query[(__bridge __strong id)kSecAttrProtocol] = (__bridge id)protocolTypeObject; 1127 | } 1128 | CFTypeRef authenticationTypeObject = [self authenticationTypeObject]; 1129 | if (authenticationTypeObject) { 1130 | query[(__bridge __strong id)kSecAttrAuthenticationType] = (__bridge id)authenticationTypeObject; 1131 | } 1132 | } 1133 | 1134 | #if TARGET_OS_IOS 1135 | if (_authenticationPrompt) { 1136 | if (floor(NSFoundationVersionNumber) > floor(1047.25)) { // iOS 8+ (NSFoundationVersionNumber_iOS_7_1) 1137 | query[(__bridge __strong id)kSecUseOperationPrompt] = _authenticationPrompt; 1138 | } else { 1139 | NSLog(@"%@", @"Unavailable 'authenticationPrompt' attribute on iOS versions prior to 8.0."); 1140 | } 1141 | } 1142 | #endif 1143 | 1144 | return query; 1145 | } 1146 | 1147 | - (NSMutableDictionary *)attributesWithKey:(NSString *)key value:(NSData *)value error:(NSError *__autoreleasing *)error 1148 | { 1149 | NSMutableDictionary *attributes; 1150 | 1151 | if (key) { 1152 | attributes = [self query]; 1153 | attributes[(__bridge __strong id)kSecAttrAccount] = key; 1154 | } else { 1155 | attributes = [[NSMutableDictionary alloc] init]; 1156 | } 1157 | 1158 | attributes[(__bridge __strong id)kSecValueData] = value; 1159 | 1160 | #if TARGET_OS_IOS 1161 | double iOS_7_1_or_10_9_2 = 1047.25; // NSFoundationVersionNumber_iOS_7_1 1162 | #else 1163 | double iOS_7_1_or_10_9_2 = 1056.13; // NSFoundationVersionNumber10_9_2 1164 | #endif 1165 | CFTypeRef accessibilityObject = [self accessibilityObject]; 1166 | if (_authenticationPolicy && accessibilityObject) { 1167 | if (floor(NSFoundationVersionNumber) > floor(iOS_7_1_or_10_9_2)) { // iOS 8+ or OS X 10.10+ 1168 | CFErrorRef securityError = NULL; 1169 | SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibilityObject, (SecAccessControlCreateFlags)_authenticationPolicy, &securityError); 1170 | if (securityError) { 1171 | NSError *e = (__bridge NSError *)securityError; 1172 | NSLog(@"error: [%@] %@", @(e.code), e.localizedDescription); 1173 | if (error) { 1174 | *error = e; 1175 | CFRelease(accessControl); 1176 | return nil; 1177 | } 1178 | } 1179 | if (!accessControl) { 1180 | NSString *message = NSLocalizedString(@"Unexpected error has occurred.", nil); 1181 | NSError *e = [self.class unexpectedError:message]; 1182 | if (error) { 1183 | *error = e; 1184 | } 1185 | return nil; 1186 | } 1187 | attributes[(__bridge __strong id)kSecAttrAccessControl] = (__bridge_transfer id)accessControl; 1188 | } else { 1189 | #if TARGET_OS_IOS 1190 | NSLog(@"%@", @"Unavailable 'Touch ID integration' on iOS versions prior to 8.0."); 1191 | #else 1192 | NSLog(@"%@", @"Unavailable 'Touch ID integration' on OS X versions prior to 10.10."); 1193 | #endif 1194 | } 1195 | } else { 1196 | if (floor(NSFoundationVersionNumber) <= floor(iOS_7_1_or_10_9_2) && _accessibility == UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly) { 1197 | #if TARGET_OS_IOS 1198 | NSLog(@"%@", @"Unavailable 'UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly' attribute on iOS versions prior to 8.0."); 1199 | #else 1200 | NSLog(@"%@", @"Unavailable 'UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly' attribute on OS X versions prior to 10.10."); 1201 | #endif 1202 | } else { 1203 | if (accessibilityObject) { 1204 | attributes[(__bridge __strong id)kSecAttrAccessible] = (__bridge id)accessibilityObject; 1205 | } 1206 | } 1207 | } 1208 | 1209 | if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+ 1210 | attributes[(__bridge __strong id)kSecAttrSynchronizable] = @(_synchronizable); 1211 | } 1212 | 1213 | return attributes; 1214 | } 1215 | 1216 | #pragma mark - 1217 | 1218 | - (CFTypeRef)itemClassObject 1219 | { 1220 | switch (_itemClass) { 1221 | case UICKeyChainStoreItemClassGenericPassword: 1222 | return kSecClassGenericPassword; 1223 | case UICKeyChainStoreItemClassInternetPassword: 1224 | return kSecClassInternetPassword; 1225 | default: 1226 | return nil; 1227 | } 1228 | } 1229 | 1230 | - (CFTypeRef)protocolTypeObject 1231 | { 1232 | switch (_protocolType) { 1233 | case UICKeyChainStoreProtocolTypeFTP: 1234 | return kSecAttrProtocolFTP; 1235 | case UICKeyChainStoreProtocolTypeFTPAccount: 1236 | return kSecAttrProtocolFTPAccount; 1237 | case UICKeyChainStoreProtocolTypeHTTP: 1238 | return kSecAttrProtocolHTTP; 1239 | case UICKeyChainStoreProtocolTypeIRC: 1240 | return kSecAttrProtocolIRC; 1241 | case UICKeyChainStoreProtocolTypeNNTP: 1242 | return kSecAttrProtocolNNTP; 1243 | case UICKeyChainStoreProtocolTypePOP3: 1244 | return kSecAttrProtocolPOP3; 1245 | case UICKeyChainStoreProtocolTypeSMTP: 1246 | return kSecAttrProtocolSMTP; 1247 | case UICKeyChainStoreProtocolTypeSOCKS: 1248 | return kSecAttrProtocolSOCKS; 1249 | case UICKeyChainStoreProtocolTypeIMAP: 1250 | return kSecAttrProtocolIMAP; 1251 | case UICKeyChainStoreProtocolTypeLDAP: 1252 | return kSecAttrProtocolLDAP; 1253 | case UICKeyChainStoreProtocolTypeAppleTalk: 1254 | return kSecAttrProtocolAppleTalk; 1255 | case UICKeyChainStoreProtocolTypeAFP: 1256 | return kSecAttrProtocolAFP; 1257 | case UICKeyChainStoreProtocolTypeTelnet: 1258 | return kSecAttrProtocolTelnet; 1259 | case UICKeyChainStoreProtocolTypeSSH: 1260 | return kSecAttrProtocolSSH; 1261 | case UICKeyChainStoreProtocolTypeFTPS: 1262 | return kSecAttrProtocolFTPS; 1263 | case UICKeyChainStoreProtocolTypeHTTPS: 1264 | return kSecAttrProtocolHTTPS; 1265 | case UICKeyChainStoreProtocolTypeHTTPProxy: 1266 | return kSecAttrProtocolHTTPProxy; 1267 | case UICKeyChainStoreProtocolTypeHTTPSProxy: 1268 | return kSecAttrProtocolHTTPSProxy; 1269 | case UICKeyChainStoreProtocolTypeFTPProxy: 1270 | return kSecAttrProtocolFTPProxy; 1271 | case UICKeyChainStoreProtocolTypeSMB: 1272 | return kSecAttrProtocolSMB; 1273 | case UICKeyChainStoreProtocolTypeRTSP: 1274 | return kSecAttrProtocolRTSP; 1275 | case UICKeyChainStoreProtocolTypeRTSPProxy: 1276 | return kSecAttrProtocolRTSPProxy; 1277 | case UICKeyChainStoreProtocolTypeDAAP: 1278 | return kSecAttrProtocolDAAP; 1279 | case UICKeyChainStoreProtocolTypeEPPC: 1280 | return kSecAttrProtocolEPPC; 1281 | case UICKeyChainStoreProtocolTypeNNTPS: 1282 | return kSecAttrProtocolNNTPS; 1283 | case UICKeyChainStoreProtocolTypeLDAPS: 1284 | return kSecAttrProtocolLDAPS; 1285 | case UICKeyChainStoreProtocolTypeTelnetS: 1286 | return kSecAttrProtocolTelnetS; 1287 | case UICKeyChainStoreProtocolTypeIRCS: 1288 | return kSecAttrProtocolIRCS; 1289 | case UICKeyChainStoreProtocolTypePOP3S: 1290 | return kSecAttrProtocolPOP3S; 1291 | default: 1292 | return nil; 1293 | } 1294 | } 1295 | 1296 | - (CFTypeRef)authenticationTypeObject 1297 | { 1298 | switch (_authenticationType) { 1299 | case UICKeyChainStoreAuthenticationTypeNTLM: 1300 | return kSecAttrAuthenticationTypeNTLM; 1301 | case UICKeyChainStoreAuthenticationTypeMSN: 1302 | return kSecAttrAuthenticationTypeMSN; 1303 | case UICKeyChainStoreAuthenticationTypeDPA: 1304 | return kSecAttrAuthenticationTypeDPA; 1305 | case UICKeyChainStoreAuthenticationTypeRPA: 1306 | return kSecAttrAuthenticationTypeRPA; 1307 | case UICKeyChainStoreAuthenticationTypeHTTPBasic: 1308 | return kSecAttrAuthenticationTypeHTTPBasic; 1309 | case UICKeyChainStoreAuthenticationTypeHTTPDigest: 1310 | return kSecAttrAuthenticationTypeHTTPDigest; 1311 | case UICKeyChainStoreAuthenticationTypeHTMLForm: 1312 | return kSecAttrAuthenticationTypeHTMLForm; 1313 | case UICKeyChainStoreAuthenticationTypeDefault: 1314 | return kSecAttrAuthenticationTypeDefault; 1315 | default: 1316 | return nil; 1317 | } 1318 | } 1319 | 1320 | - (CFTypeRef)accessibilityObject 1321 | { 1322 | switch (_accessibility) { 1323 | case UICKeyChainStoreAccessibilityWhenUnlocked: 1324 | return kSecAttrAccessibleWhenUnlocked; 1325 | case UICKeyChainStoreAccessibilityAfterFirstUnlock: 1326 | return kSecAttrAccessibleAfterFirstUnlock; 1327 | case UICKeyChainStoreAccessibilityAlways: 1328 | return kSecAttrAccessibleAlways; 1329 | case UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly: 1330 | return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; 1331 | case UICKeyChainStoreAccessibilityWhenUnlockedThisDeviceOnly: 1332 | return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; 1333 | case UICKeyChainStoreAccessibilityAfterFirstUnlockThisDeviceOnly: 1334 | return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; 1335 | case UICKeyChainStoreAccessibilityAlwaysThisDeviceOnly: 1336 | return kSecAttrAccessibleAlwaysThisDeviceOnly; 1337 | default: 1338 | return nil; 1339 | } 1340 | } 1341 | 1342 | + (NSError *)argumentError:(NSString *)message 1343 | { 1344 | NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:UICKeyChainStoreErrorInvalidArguments userInfo:@{NSLocalizedDescriptionKey: message}]; 1345 | NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription); 1346 | return error; 1347 | } 1348 | 1349 | + (NSError *)conversionError:(NSString *)message 1350 | { 1351 | NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:-67594 userInfo:@{NSLocalizedDescriptionKey: message}]; 1352 | NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription); 1353 | return error; 1354 | } 1355 | 1356 | + (NSError *)securityError:(OSStatus)status 1357 | { 1358 | NSString *message = @"Security error has occurred."; 1359 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 1360 | CFStringRef description = SecCopyErrorMessageString(status, NULL); 1361 | if (description) { 1362 | message = (__bridge_transfer NSString *)description; 1363 | } 1364 | #endif 1365 | NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:status userInfo:@{NSLocalizedDescriptionKey: message}]; 1366 | NSLog(@"OSStatus error: [%@] %@", @(error.code), error.localizedDescription); 1367 | return error; 1368 | } 1369 | 1370 | + (NSError *)unexpectedError:(NSString *)message 1371 | { 1372 | NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:-99999 userInfo:@{NSLocalizedDescriptionKey: message}]; 1373 | NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription); 1374 | return error; 1375 | } 1376 | 1377 | @end 1378 | 1379 | @implementation UICKeyChainStore (Deprecation) 1380 | 1381 | - (void)synchronize 1382 | { 1383 | // Deprecated, calling this method is no longer required 1384 | } 1385 | 1386 | - (BOOL)synchronizeWithError:(NSError *__autoreleasing *)error 1387 | { 1388 | // Deprecated, calling this method is no longer required 1389 | return true; 1390 | } 1391 | 1392 | @end 1393 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Pham Tan Long 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unity-ios-keychain-plugin 2 | 3 | Requires Unity 5.0.0 or higher. 4 | 5 | The iOS Keychain Plugin gives access to the iOS keychain through Unity and lets you store game data or player information that can be automatically synced across devices and persist between installations. 6 | 7 | Common uses include: 8 | ▪ Game save file 9 | ▪ Preferences 10 | ▪ Passwords 11 | ▪ Unique Identifier to consistently identify a user, even after they uninstall/reinstall the application or switch to a new device. 12 | 13 | Build IOS Project: 14 | + Added Security.Framework to your project in XCode before build. 15 | 16 | 17 | --------------------------------------------------------------------------------