Problem using KeychainItemWrapper
Asked Answered
S

4

5

I use the following code to retrieve the login credentials from the iPhone keychain:

KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Test" accessGroup:nil];
NSString *username = [wrapper objectForKey:(id)kSecAttrAccount];
NSString *password = [wrapper objectForKey:(id)kSecValueData];
[wrapper release];

I'm under the impression that the first time a user launches the app, neither username nor password could be retrieved from the keychain, so username and password should be equal to nil. I, however, was unable to print out any of these variables using NSLog.

Any suggestions?

Sebastiansebastiano answered 29/8, 2010 at 16:51 Comment(4)
What were you able to print? Why don't you set a breakpoint and inspect the objects while running?Glasswort
Nothing. Nothing shows up when I tried to print the objects. Inspecting them only display the address in the form of 0xSOMETHING.Sebastiansebastiano
@Sebastiansebastiano stupid question, but have you verified that wrapper is not nil? Also, I agree with vfn about setting break points...Delight
You should accept JRG's answer. The values are initially set to @"" and not nil.Hashum
A
5

Your assumption is wrong- on creation, the "kSecAttrAccount" and "kSecValueData" are NOT set to nil. They are set to an empty string (that is, ""). So, this code will return true:

[username isEqualToString:@""]    // returns true on creation
Anthropopathy answered 21/2, 2012 at 17:28 Comment(1)
Instead of doing a direct string comparison, checking for length works just as well. [username length] > 0Severance
P
8
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"Test" accessGroup:nil];
NSString *username = [wrapper objectForKey:(id)kSecAttrAccount];
NSString *password = [wrapper objectForKey:(id)kSecValueData];

// initially all these are empty
NSLog(@"username - %@", username); // username - 
NSLog(@"password - %@", password); // password - 

//if empty set your credentials
if ( [username isEqualToString:@""] ) {
    [wrapper setObject:@"your username here" forKey:(id)kSecAttrAccount];
}
if ( [password isEqualToString:@""] ) {
    [wrapper setObject:@"your password here" forKey:(id)kSecValueData];
}

//get the latest credentials - now you have the set values
username = [wrapper objectForKey:(id)kSecAttrAccount];
password = [wrapper objectForKey:(id)kSecValueData];

NSLog(@"username - %@", username); // username - your username here
NSLog(@"password - %@", password); // password - your password here

// reset your keychain items - if  needed
[wrapper resetKeychainItem];
[wrapper release];
Prestidigitation answered 31/8, 2010 at 4:59 Comment(2)
The part I'm not sure about is "initially all these are empty". As mentioned in my question, I couldn't get NSLog to print any of these values. I'm trying to bring up the login dialog if they're empty/nil, but it doesn't work.Sebastiansebastiano
Will these values keep when relaunching the app?Aranda
A
5

Your assumption is wrong- on creation, the "kSecAttrAccount" and "kSecValueData" are NOT set to nil. They are set to an empty string (that is, ""). So, this code will return true:

[username isEqualToString:@""]    // returns true on creation
Anthropopathy answered 21/2, 2012 at 17:28 Comment(1)
Instead of doing a direct string comparison, checking for length works just as well. [username length] > 0Severance
B
1

Same error for me, i checked the return value for writeToKeychain function in KeychainItemWrapper.m file. Return value is equal to errSecDuplicateItem. I don't know why but seems like SecItemCopyMatching function does not working properly. (For my other project working properly).

I changed codes for now and working for me: Updated codes for writeToKeychain in KeychainItemWrapper.m file:

- (void)writeToKeychain
{
    NSDictionary *attributes = NULL;
    NSMutableDictionary *updateItem = NULL;
    OSStatus result;



    if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr)
    {
        // First we need the attributes from the Keychain.
        updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
        // Second we need to add the appropriate search key/values.
        [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];

        // Lastly, we need to set up the updated attribute list being careful to remove the class.
        NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
        [tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR
        // Remove the access group if running on the iPhone simulator.
        // 
        // Apps that are built for the simulator aren't signed, so there's no keychain access group
        // for the simulator to check. This means that all apps can see all keychain items when run
        // on the simulator.
        //
        // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
        // simulator will return -25243 (errSecNoAccessForItem).
        //
        // The access group attribute will be included in items returned by SecItemCopyMatching,
        // which is why we need to remove it before updating the item.
        [tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];
#endif

        // An implicit assumption is that you can only update a single item at a time.

        result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
        NSAssert( result == noErr, @"Couldn't update the Keychain Item." );
    }
    else
    {
        // No previous item found; add the new one.

        result =  SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
        NSLog(@"%@",keychainItemData);
        NSLog(@"res : %ld",result);
        if(result == (OSStatus)errSecDuplicateItem)
        {
            NSLog(@"updttttt");
            // First we need the attributes from the Keychain.
            updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];
            // Second we need to add the appropriate search key/values.
            [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];

            // Lastly, we need to set up the updated attribute list being careful to remove the class.
            NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];
            [tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR
            // Remove the access group if running on the iPhone simulator.
            // 
            // Apps that are built for the simulator aren't signed, so there's no keychain access group
            // for the simulator to check. This means that all apps can see all keychain items when run
            // on the simulator.
            //
            // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
            // simulator will return -25243 (errSecNoAccessForItem).
            //
            // The access group attribute will be included in items returned by SecItemCopyMatching,
            // which is why we need to remove it before updating the item.
            [tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];
#endif

            // An implicit assumption is that you can only update a single item at a time.

            result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
            NSAssert( result == noErr, @"Couldn't update the Keychain Item." );
            return;
        }//if(result == errSecDuplicateItem)*
        NSAssert( result == noErr, @"Couldn't add the Keychain Item." );
    }
}
Brewmaster answered 26/8, 2012 at 14:40 Comment(0)
H
0

If the values are initially nil, using

if ( [username isEqualToString:@""] )

will evaluate to FALSE. You could use

if (!username)

instead

Hemichordate answered 11/1, 2012 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.