Using SecItemUpdate in Keychain Services
Asked Answered
K

2

7

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.

Knighton answered 21/11, 2013 at 17:51 Comment(0)
S
8

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.

Sensory answered 22/11, 2013 at 0:0 Comment(2)
Thanks yeah this is true. In the end, there is a lot wrong with my code up there, I ended up using the ARC version of Apples KeychainItemWrapper (gist.github.com/dhoerl/1170641) and it ended up being perfect for what I was trying to accomplish.Knighton
Can one just use kSecItemAdd every-time, and simply overwrite existing items? And this way never need to call kSecItemUpdate at all?Lawyer
I
3

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.

I answered 18/8, 2015 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.