0

I need to store multiple passwords/passcode with unique key in Keychain. I'm getting nil value during fetch the stored data from the keychain. Can any one help me out from this.

This is my code...

- (void)storeSensitiveDataToKeychain:(NSString *)value withKey:(NSString *)key {
    NSString *keyValue = [self fetchDataFromKeychain:key];
    if ([keyValue isEqualToString:@""] || [keyValue isEqual:[NSNull null]] || !keyValue.length || keyValue == nil) {
        [keychainClass insert:key :[value dataUsingEncoding:NSUTF8StringEncoding]];
    } else {
        [keychainClass update:key :[value dataUsingEncoding:NSUTF8StringEncoding]];
    }
}

- (NSString *)fetchDataFromKeychain:(NSString *)key {

    NSData *value = [keychainClass find:key];
    if (value == nil) {
        NSLog(@"key value is nil");
        return @"";
    } else {
        return  [[NSString alloc] initWithData:value
                                                   encoding:NSUTF8StringEncoding];
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Surezz
  • 561
  • 4
  • 12
  • If you want to store multiple passwords then the key you use must be either `username` or `email` of the user, as these key are unique so you can store it and later on retrieve the same using `email` or `username`. – Rajat Nov 07 '16 at 11:28
  • Thanks Rajat, I'm using the key as Email. Can you share any sample, If possible? – Surezz Nov 07 '16 at 11:49
  • Which class you are using for storing into keychain, like SSKeychain or any other ? – Rajat Nov 07 '16 at 12:47
  • @Rajat, No I'm using custom class and I added below. – Surezz Nov 08 '16 at 07:34

1 Answers1

0

I have followed below steps to store my own key and value to the Keychain,

It works awesome!

1.Create a NSObject Class

2.Import

#import<Security/Security.h> framework

3.In header file add,

- (id) initWithService:(NSString *) service_ withGroup:(NSString*)group_;

- (BOOL)insert:(NSString *)key dataValue:(NSData *)data;
- (BOOL)update:(NSString*)key dataValue:(NSData*)data;
- (BOOL)remove:(NSString*)key;
- (NSData*)find:(NSString*)key;

3.In Implementation file add,

- (id) initWithService:(NSString *) service_ withGroup:(NSString*)group_ {
    self =[super init];
    if(self) {
        service = [NSString stringWithString:service_];
        if(group_)
            group = [NSString stringWithFormat:@"%@.%@",[KeyChain bundleSeedID],group_];
    }
    NSLog(@"%@",group);
    return  self;
}

- (NSMutableDictionary*)prepareDict:(NSString *)key {

    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:service forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];

    //This is for sharing data across apps
    if(group != nil)
        [dict setObject:group forKey:(__bridge id)kSecAttrAccessGroup];

    return  dict;
}

- (BOOL)insert:(NSString *)key dataValue:(NSData *)data {

    NSMutableDictionary * dict =[self prepareDict:key];
    [dict setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
    if(errSecSuccess != status) {
        NSLog(@"Unable add item with key =%@ error:%d",key,(int)status);
    }
    return (errSecSuccess == status);
}

- (NSData*)find:(NSString*)key {
    NSMutableDictionary *dict = [self prepareDict:key];
    [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
        return nil;
    }

    return (__bridge NSData *)result;
}

- (BOOL)update:(NSString*)key dataValue:(NSData*)data {

    NSMutableDictionary * dictKey =[self prepareDict:key];
    NSMutableDictionary * dictUpdate =[[NSMutableDictionary alloc] init];
    [dictUpdate setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)dictKey, (__bridge CFDictionaryRef)dictUpdate);
    if(errSecSuccess != status) {
        NSLog(@"Unable add update with key =%@ error:%d",key,(int)status);
    }
    return (errSecSuccess == status);

    return YES;
}

- (BOOL)remove: (NSString*)key {
    NSMutableDictionary *dict = [self prepareDict:key];
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);
    if( status != errSecSuccess) {
        NSLog(@"Unable to remove item for key %@ with error:%d",key,(int)status);
        return NO;
    }
    return  YES;
}

+ (NSString *)bundleSeedID {
    NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
                           (__bridge NSString *)kSecClassGenericPassword, (__bridge NSString *)kSecClass,
                           @"bundleSeedID", kSecAttrAccount,
                           @"Service_name", kSecAttrService,
                           (id)kCFBooleanTrue, kSecReturnAttributes,
                           nil];
    CFDictionaryRef result = nil;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status == errSecItemNotFound)
        status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status != errSecSuccess)
        return nil;
    NSString *accessGroup = [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
    NSArray *components = [accessGroup componentsSeparatedByString:@"."];
    NSString *bundleSeedID = [[components objectEnumerator] nextObject];
    CFRelease(result);
    return bundleSeedID;
}

@end

You can call these below methods, whereever you need.

Make sure your key name might be same.

#pragma mark - Keychain Delegates

- (void)storeSensitiveDataToKeychain:(NSString *)value withKey:(NSString *)key {
    [keychainClass insert:[key lowercaseString] :[value dataUsingEncoding:NSUTF8StringEncoding]];
}

- (NSString *)fetchDataFromKeychain:(NSString *)key {
    NSString *keyValue = [[NSString alloc] initWithData:[keychainClass find:[key lowercaseString]] encoding:NSUTF8StringEncoding];
    if (keyValue.length > 0 && ![keyValue isEqual:[NSNull null]]) {
        return keyValue;
    }
    return @"";
}
Surezz
  • 561
  • 4
  • 12