Uniruddh
Uniruddh

Reputation: 4436

Unable to store values in keychain correctly

I am using the following to store values in the keychain:

KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];       
[keychain setObject:[object valueForKey:@"token"] forKey:(__bridge id)(kSecValueData)];
[keychain setObject:[object valueForKey:@"usr_id"] forKey:(__bridge id)(kSecAttrAccount)];

Here is my code for retrieving values:

KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];   
NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);

I have also included the security framework.

I have searched stackoverflow and google but am not able to get values in NSLog.

What could be the reason?

EDIT :

One can also Provide me other information if anyone have, in order to work with keychain?

Upvotes: 2

Views: 3989

Answers (3)

Dheeraj D
Dheeraj D

Reputation: 4451

If you are using xCode 8.0 please do below :

Project Navigator -> Select Target -> Capabilities 

Enable KeyChain Sharing ON

enter image description here

Upvotes: 0

David Hoerl
David Hoerl

Reputation: 41652

What follows is working code that lets you maintain a dictionary of data tied to a primary "key" (an EMail address here which could be any primary identifier in your app).

The other keys get stored in a dictionary of arbitrary size, and get placed in what would be the "password" keyChain object.

First, I put this at the bottom of my AppDelegate.h file:

#define kEmail          @"email"
#define kPassword       @"pass"
#define kSomeOtherItemA @"a_item"
#define kSomeOtherItemB @"b_item"

@interface AppDelegate (Keychain)

- (NSDictionary *)keychainDictionary;                                   // current keyChain dictionary
- (void)eraseKeychain;                                                  // total wipe it
- (void)erasePassword;                                                  // wipe the password

@end

You can add special methods here as you see fit. By placing this is the AppDelegate.h file, any other class importing that file can use these methods. If you want it private move it to your AppDelegate.m file

This goes in your AppDelegate.m file, up top:

@interface AppDelegate (KeychainPrivate)

- (void)updateKeychainItem:(NSString *)item forKey:(NSString *)key; //the password, or itemA, or itemB
- (id)keychainItemForKey:(NSString *)key; // the password, itemA, itemB, ...

@end

This can go at the bottom of AppDelegate.m or in a separate file:

static char *kcIdentifier = "foo"; // use any string you want instead of "foo"

@implementation AppDelegate (Keychain)

- (KeychainItemWrapper *)keyChainItemWrapper
{
    static dispatch_once_t pred;
    static KeychainItemWrapper *kciw;

    dispatch_once(&pred, ^
        {
            kciw = [[KeychainItemWrapper alloc] initWithIdentifier:kcIdentifier accessGroup:nil];
        } );

    return kciw;
}


- (NSDictionary *)keychainDictionary
{
    NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithCapacity:3];
    KeychainItemWrapper *kcItem = [self keyChainItemWrapper];

    NSString *emailAddr = [kcItem objectForKey:(__bridge id)kSecAttrAccount];
    if([emailAddr length]) {
        NSData *data = [kcItem objectForKey:(__bridge id)kSecValueData];
        if([data length]) {
            NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
            NSDictionary *dict = [kua decodeObject];
            [kua finishDecoding];
            [mDict addEntriesFromDictionary:dict];
        }
        mDict[kEmail] = emailAddr;  // last in case it got in the dictionary somehow
    }
    return mDict;
}

- (void)eraseKeychain
{
    KeychainItemWrapper *kcItem = [self keyChainItemWrapper];
    [kcItem resetKeychainItem];
}

- (void)erasePassword
{
    [self updateKeychainItem:@"" forKey:kPassword];
}

@end

@implementation AppDelegate (KeychainPrivate)

- (void)updateKeychainItem:(NSString *)item forKey:(NSString *)key
{
    KeychainItemWrapper *kcItem = [self keyChainItemWrapper];
    // NSLog(@"SET KC key=%@ ITEM %@", key, item);

    // NSLog(@"CURRENT KEYCHAIN: %@", [self keychainDictionary]);

    if([key isEqualToString:kEmail]) {
        NSString *emailAddr = [kcItem objectForKey:(__bridge id)kSecAttrAccount];
        if(![emailAddr isEqualToString:item]) {
            [kcItem setObject:item forKey:(__bridge id)kSecAttrAccount];
            // NSLog(@"SET KC Account ITEM %@", item);
        }
        // NSLog(@"KC Account ITEM %@ ALREADY SET", item);
    } else {
        NSData *data = [kcItem objectForKey:(__bridge id)kSecValueData];
        //NSLog(@"KC get DATA len=%d", [data length]);
        NSDictionary *dict;
        if([data length]) {
            NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
            dict = [kua decodeObject];
            [kua finishDecoding];
        } else {
            dict = [NSDictionary dictionary];
        }
        //NSLog(@"KC OLD DICT %@", dict);

        if(![item isEqualToString:dict[key]]) {
            //NSLog(@"KC DATA NOT EQUAL");
            NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithDictionary:dict];
            mDict[key] = item;

            NSMutableData *tmpData = [NSMutableData dataWithCapacity:256];
            NSKeyedArchiver *ka = [[NSKeyedArchiver alloc] initForWritingWithMutableData:tmpData];
            [ka encodeObject:mDict];
            [ka finishEncoding];
            //NSLog(@"KC ENCODE MDICT %@", mDict);

            [kcItem setObject:tmpData forKey:(__bridge id)kSecValueData];
            //NSLog(@"SET KC DATA LEN=%d", [tmpData length]);
        }
    }
    //NSLog(@"JUST UPDATED KEYCHAIN KEY=%@ val=%@ read dict back=%@", key, item, [self keychainDictionary]);
}

- (id)keychainItemForKey:(NSString *)key
{
    NSDictionary *dict = [self keychainDictionary];
    return dict[key];
}

@end

You also need the KeyChainItemWrapper class. You can get an ARCified and slightly modified (so as to handle a dictionary) KeyChainItemWrapper version. You can really use any KeyChainWrapper file you want, but need to make this change (which is the only change to Apple's code other than ARCifying it):

// Default data for keychain item.
#ifndef PASSWORD_USES_DATA
    [keychainItemData setObject:@"" forKey:(__bridge id)kSecValueData];
#else
    [keychainItemData setObject:[NSData data] forKey:(__bridge id)kSecValueData];
#endif

As you can see, there are lots of log messages you can uncomment to see it in action if you want.

This code was used in a shipping App.

Upvotes: 1

Tancrede Chazallet
Tancrede Chazallet

Reputation: 7255

I use an answer cause comment are limited... I tried the following code out of a new project, downloading KeychainItemWrapper and linking Security.framework

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    {
        KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
        [keychain setObject:@"aaaa" forKey:(__bridge id)(kSecValueData)];
        [keychain setObject:@"bbbb" forKey:(__bridge id)(kSecAttrAccount)];
        NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
        NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
    }

    {
        KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
        NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
        NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
    }

    // Override point for customization after application launch.
    return YES;
}

Everything works perfectly. I guess you need to provide more of your code in order to find an answer. Here my logs :

2013-11-19 17:11:08.378 test[3430:a0b] TOKEN:aaaa
2013-11-19 17:11:08.379 test[3430:a0b] USER NAME:bbbb
2013-11-19 17:11:08.380 test[3430:a0b] TOKEN:aaaa
2013-11-19 17:11:08.381 test[3430:a0b] USER NAME:bbbb

Upvotes: 3

Related Questions