Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SecItemUpdate in Keychain Services

I have the following code to create a keychain item in the keychain:

    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    [dict setObject: (__bridge id) kSecClassGenericPassword  forKey: (__bridge id) kSecClass];
    [dict setObject: MYKEY           forKey: (__bridge id) kSecAttrService];
    [dict setObject: @"0" forKey: (__bridge id) kSecValueData];
    SecItemAdd ((__bridge CFDictionaryRef) dict, NULL);

Which works fine. Can anyone give the syntax for what exactly to put for SecItemUpdate if I want to change this item?

UPDATE: with the following:

NSMutableDictionary *query = [NSMutableDictionary dictionary];
NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary];

[query setObject: (__bridge id) kSecClassGenericPassword forKey: (__bridge id) kSecClass];
[query setObject: MYKEY forKey: (__bridge id) kSecAttrService];
[query setObject: (id) kCFBooleanTrue forKey: (__bridge id) kSecReturnData];

NSString *numberOfBalloonsString = [NSString stringWithFormat:@"%d", numberOfBalloonsUsed];
NSData *numberOfBalloonsData = [numberOfBalloonsString dataUsingEncoding:NSUTF8StringEncoding];

[attributesToUpdate setObject: numberOfBalloonsData forKey:(__bridge id)kSecValueData];

OSStatus error = SecItemUpdate ((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) attributesToUpdate);
NSLog(@"Error #: %ld", error);

I'm getting the error code -50 =

One or more parameters passed to the function were not valid.

like image 618
Ser Pounce Avatar asked Nov 21 '13 17:11

Ser Pounce


2 Answers

SecItemUpdate is terribly documented.

The query parameter of SecItemUpdate is documented as a query (as used in other functions) as well as the vague statement: "Specify the items whose values you wish to change". This seems to imply that you must include the existing attribute value in this dictionary that you want to change but I don't think you do. I've found you can use the same query you use to get attributes for the item you want to update.

The attributes parameter should be the result of SecItemCopyMatching with the kSecValueData key and value added and any attributes changed.

like image 154
Fruity Geek Avatar answered Oct 12 '22 23:10

Fruity Geek


A late answer, but an answer nonetheless:

I've been struggling with updating items in the keychain as well, my context was a little different though.


What happened:

I could add a keychain item with success (using SecItemAdd), but calling SecItemUpdate on the same item failed with the notorious errSecParam -50.
What was even worse; if the keychain item already existed (hence I called SecItemUpdate immediately), the update went through with no problems at all.
I've got absolutely no idea as of why that happened...

How I fixed it:

Quite simple actually, I just removed "params" until the big bad -50 was satisfied. This happened when I removed the kSecClass from the dictionary I retrieved from kSecItemCopyMatching.
Here's my code:

 // If the item already exists, we update it instead
if (SecItemCopyMatching((__bridge CFDictionaryRef)self.searchQueryDict, (CFTypeRef *)&foundItem) == errSecSuccess) {
    NSMutableDictionary *updateDict = (__bridge NSMutableDictionary *)foundItem;
    [updateDict addEntriesFromDictionary:dictToSave];
    [updateDict removeObjectForKey:(__bridge id)kSecClass];

    OSStatus updateSuccess = SecItemUpdate((__bridge CFDictionaryRef)self.updateQueryDict,
                                           (__bridge CFDictionaryRef)updateDict);
    NSAssert(updateSuccess == errSecSuccess, @"Couldn't save the dirty info to the keychain, might want to log the updateSuccess (%d)", updateSuccess);
}

As a reference I used the following dictionaries

self.searchQueryDict contained:

(__bridge id)kSecClass              : (__bridge id)kSecClassGenericPassword              
(__bridge id)kSecAttrService        : service                                            
(__bridge id)kSecAttrGeneric        : [identifier dataUsingEncoding:NSUTF8StringEncoding]
(__bridge id)kSecMatchLimit         : (__bridge id)kSecMatchLimitOne                     
(__bridge id)kSecReturnAttributes   : (__bridge id)kCFBooleanTrue                        
(__bridge id)kSecReturnData         : (__bridge id)kCFBooleanTrue                        

self.updateQueryDict contained:

(__bridge id)kSecClass       : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : service,
(__bridge id)kSecAttrGeneric : [identifier dataUsingEncoding:NSUTF8StringEncoding]

dictToSave should contain the values (in the correct format) which needs to change

Removing the kSecClass fixed the problem for me.

like image 32
Gee.E Avatar answered Oct 13 '22 01:10

Gee.E