Skip to content

Instantly share code, notes, and snippets.

@hpique
Last active December 21, 2015 08:58
Show Gist options
  • Save hpique/6281461 to your computer and use it in GitHub Desktop.
Save hpique/6281461 to your computer and use it in GitHub Desktop.
A simple utility class to store/retrieve a password in/from the iOS Keychain.
//
// RMPasswordStore.m
//
// Created by Hermes Pique on 8/9/13.
//
//
#import <Foundation/Foundation.h>
#import <Security/Security.h>
@interface RMPasswordStore : NSObject
+ (RMPasswordStore*) defaultStore;
@property (nonatomic, copy) NSString *password;
@end
@implementation RMPasswordStore
+ (RMPasswordStore*) defaultStore
{
static RMPasswordStore *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
- (void)setPassword:(NSString *)password
{
NSMutableDictionary *searchDictionary = [RMPasswordStore dictionaryForPasswordSearch];
OSStatus status;
NSData *ignore;
if (SecItemCopyMatching((CFDictionaryRef)searchDictionary, (CFTypeRef *)&ignore) == errSecSuccess)
{ // Update
if (password == nil)
{
status = SecItemDelete((CFDictionaryRef)searchDictionary);
}
else
{
NSMutableDictionary *updateDictionary = [NSMutableDictionary dictionary];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:passwordData forKey:(id)kSecValueData];
status = SecItemUpdate((CFDictionaryRef)searchDictionary, (CFDictionaryRef)updateDictionary);
}
if (status != errSecSuccess)
{
NSLog(@"RMPasswordStore: failed to update password with error %ld", status);
}
}
else if (password != nil)
{ // Add
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:passwordData forKey:(id)kSecValueData];
status = SecItemAdd((CFDictionaryRef)searchDictionary, NULL);
if (status != errSecSuccess)
{
NSLog(@"RMPasswordStore: failed to add password with error %ld", status);
}
}
}
-(NSString*)password
{
NSMutableDictionary *searchDictionary = [RMPasswordStore dictionaryForPasswordSearch];
[searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
NSData *passwordData = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary, (CFTypeRef *)&passwordData);
if (status != errSecSuccess)
{
NSLog(@"RMPasswordStore: failed to get password with error %ld", status);
return nil;
}
NSString *password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding];
return [password autorelease];
}
#pragma mark - Private
+ (NSMutableDictionary *)dictionaryForPasswordSearch
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
static NSString *identifier = @"password";
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:encodedIdentifier forKey:(id)kSecAttrGeneric];
[dictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
NSString *serviceName = [NSBundle mainBundle].bundleIdentifier;
[dictionary setObject:serviceName forKey:(id)kSecAttrService];
return dictionary;
}
@end
@hpique
Copy link
Author

hpique commented Aug 20, 2013

I wanted to store a password securely in the simplest manner possible:

[RMPasswordStore defaultStore].password = @"ThePassword"; // to add/update
[RMPasswordStore defaultStore].password = nil; // to remove

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment