Last active
March 27, 2021 23:31
-
-
Save paulmelnikow/4645764 to your computer and use it in GitHub Desktop.
Working Objective-C keychain library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NSString *const kKeychainErrorDomain = @"kKeychainErrorDomain" | |
NSString * ServiceName = @"My Service"; | |
+ (NSError *) errorWithStatus:(OSStatus) status { | |
if (errSecSuccess == status) return nil; | |
#if __has_feature(objc_arc) | |
NSString *message = (__bridge_transfer NSString *)SecCopyErrorMessageString(status, NULL); | |
#else | |
NSString *message = [(id) SecCopyErrorMessageString(status, NULL) autorelease]; | |
#endif | |
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:message | |
forKey:NSLocalizedDescriptionKey]; | |
return [NSError errorWithDomain:kKeychainErrorDomain | |
code:status | |
userInfo:userInfo]; | |
} | |
+ (NSString *) keychainAccountNameForUserName:(NSString *) userName serverName:(NSString *) serverName { | |
return [NSString stringWithFormat:@"%@@%@", userName, serverName]; | |
} | |
+ (NSString *) passwordForUserName:(NSString *) userName serverName:(NSString *) serverName error:(NSError * __autoreleasing *) error { | |
NSString *accountName = [self.class keychainAccountNameForUserName:userName serverName:serverName]; | |
void * resultBytes = NULL; UInt32 resultLength = 0; | |
OSStatus status = SecKeychainFindGenericPassword(NULL, | |
(UInt32)ServiceName.length, | |
[ServiceName cStringUsingEncoding:NSUTF8StringEncoding], | |
(UInt32)accountName.length, | |
[accountName cStringUsingEncoding:NSUTF8StringEncoding], | |
&resultLength, | |
&resultBytes, | |
NULL); | |
if (errSecItemNotFound == status) { | |
return nil; | |
} else if (errSecSuccess != status) { | |
if (error) *error = [self errorWithStatus:status]; | |
return nil; | |
} | |
NSString *result = [[NSString alloc] initWithBytes:resultBytes | |
length:resultLength | |
encoding:NSUTF8StringEncoding]; | |
SecKeychainItemFreeContent(NULL, resultBytes); | |
return result; | |
} | |
+ (BOOL) savePassword:(NSString *) password forUserName:(NSString *) userName serverName:(NSString *) serverName error:(NSError * __autoreleasing *) error { | |
NSString *accountName = [self.class keychainAccountNameForUserName:userName serverName:serverName]; | |
if (!password || !accountName) return NO; | |
OSStatus status = SecKeychainAddGenericPassword(NULL, | |
(UInt32)ServiceName.length, | |
[ServiceName cStringUsingEncoding:NSUTF8StringEncoding], | |
(UInt32)accountName.length, | |
[accountName cStringUsingEncoding:NSUTF8StringEncoding], | |
(UInt32)password.length, | |
[password cStringUsingEncoding:NSUTF8StringEncoding], | |
NULL); | |
if (errSecSuccess == status) return YES; | |
if (errSecDuplicateItem == status) { | |
SecKeychainItemRef keyChainItem; | |
status = SecKeychainFindGenericPassword(NULL, | |
(UInt32)ServiceName.length, | |
[ServiceName cStringUsingEncoding:NSUTF8StringEncoding], | |
(UInt32)accountName.length, | |
[accountName cStringUsingEncoding:NSUTF8StringEncoding], | |
NULL, | |
NULL, | |
&keyChainItem); | |
if (errSecSuccess == status) { | |
status = SecKeychainItemModifyAttributesAndData(keyChainItem, | |
NULL, | |
(UInt32)password.length, | |
[password cStringUsingEncoding:NSUTF8StringEncoding]); | |
CFRelease(keyChainItem); | |
if (errSecSuccess == status) return YES; | |
} | |
} | |
if (error) *error = [self errorWithStatus:status]; | |
return NO; | |
} | |
+ (BOOL) clearPasswordForUserName:(NSString *) userName serverName:(NSString *) serverName error:(NSError * __autoreleasing *) error { | |
NSString *accountName = [self.class keychainAccountNameForUserName:userName serverName:serverName]; | |
if (!accountName) return NO; | |
SecKeychainItemRef keyChainItem; | |
OSStatus status = SecKeychainFindGenericPassword(NULL, | |
(UInt32)ServiceName.length, | |
[ServiceName cStringUsingEncoding:NSUTF8StringEncoding], | |
(UInt32)accountName.length, | |
[accountName cStringUsingEncoding:NSUTF8StringEncoding], | |
NULL, | |
NULL, | |
&keyChainItem); | |
if (errSecSuccess == status) { | |
status = SecKeychainItemDelete(keyChainItem); | |
CFRelease(keyChainItem); | |
if (errSecSuccess == status) return YES; | |
} | |
if (error) *error = [self errorWithStatus:status]; | |
return NO; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment