Error saving in the keychain with iphone sdk
Asked Answered
R

7

30

I use the Apple wraper for the keychain, and try to save a item on it (running in simulator, ios 4.1).

I have not experience with the keychain before.

I get this error:

Couldn't add the Keychain Item. Error - 25299

In KeychainItemWrapper.m line 304:

// No previous item found; add the new one.
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @"Couldn't add the Keychain Item." );

This is how I do the save:

- (void) saveKey:(NSString *)key value:(NSString *)value {
    KeychainItemWrapper *keyItem = [[KeychainItemWrapper alloc] initWithIdentifier:key accessGroup:nil];
    [keyItem setObject:value forKey:(id)kSecValueData];
    [keyItem release];
}

And this are the values that the api try to save:

<CFBasicHash 0x7231f60 [0x320d380]>{type = mutable dict, count = 5,
entries =>
2 : <CFString 0x2e6eb98 [0x320d380]>{contents = "labl"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
3 : <CFString 0x2e6efb8 [0x320d380]>{contents = "v_Data"} = <CFString 0x727de60 [0x320d380]>{contents = "dit8"}
4 : <CFString 0x2e6ebc8 [0x320d380]>{contents = "acct"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
5 : <CFString 0x2e6eb58 [0x320d380]>{contents = "desc"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
6 : <CFString 0x2e6ebe8 [0x320d380]>{contents = "gena"} = <CFString 0x2ffd08 [0x320d380]>{contents = "userCode"}
}
Rhaetian answered 29/11, 2010 at 23:22 Comment(2)
I'm stuck with this, so I think a bounty is a good idea. Same errorCode but iOS4.3 But I guess this is a PEBKAC problem, and no problem with the sdk.Czarism
This problem occurred to me when I added [keychainItemData setObject:@"" forKey:(__bridge id)kSecAttrService]; in - (void)resetKeychainItem. Then when I was reading user379075's answer below, it rang a bell that if you 'reset' it, you also have to set it and v.v.Joli
M
62

I know this is from several months ago, but I just had the same problem and it was painful so I thought I'd share. I solved it by adding this line:

[self.keychainItemWrapper setObject:@"MY_APP_CREDENTIALS" forKey:(id)kSecAttrService];
//@"MY_APP_CREDENTIALS" can be any string.

I found this blog entry very helpful: "In database terms you could think of their being a unique index on the two attributes kSecAttrAccount, kSecAttrService requiring the combination of those two attributes to be unique for each entry in the keychain." (from http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html).

Also, in Apple's example project using this code, they instantiate KeychainItemWrapper in the app delegate. I don't know if it's necessary, but I like to follow their examples as closely as possible:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
//there will be some standard code here.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MY_APP_CREDENTIALS" accessGroup:nil];
self.keychainWrapper = wrapper;
[self.keychainWrapper setObject:@"MYOBJECT" forKey:(id)kSecAttrService];
[wrapper release];
}

I think this is a bug in the wrapper code. The logic basically says "Does this entry exist already? No, it doesn't. OK, I'll add it. Oops, you can't add it because it's already there."

You may also need to set kSecAttrAccount; I've never tried it without also setting this value since it's intended to save the username that goes with the password:

[self.wrapper setObject:txtUserName.text forKey:(id)kSecAttrAccount];   
Monkfish answered 23/9, 2011 at 14:24 Comment(2)
Worked fine after adding the MY_APP_CREDENTIALS statement. Thanks a lot :)Vasiliu
It's important to note, I think, that when looking up keychain items, you should provide ONLY the account and service attributes to search on. If you provide more attributes, the system will search for a match on all of them, so you might not get anything back even when an item already exists for that account and service, so attempting to add and entry with that account and service to the keychain will fail.Lulu
M
9

According to the documentation, the error -25299 you are getting is "errSecDuplicateItem", meaning that the item you are trying to add already exists. Looking at the linked code for KeychainItemWrapper, I would guess that the SecItemCopyMatching call is failing with an error other than errSecItemNotFound (–25300).

Malign answered 8/4, 2011 at 13:21 Comment(0)
P
6

You can easily store and get back values with keychain using SFHFKeychainUtils by Buzz Andersen.

  1. Download and copy in your project SFHFKeychainUtils.h and .m
  2. Add Security.framework to your Framework folder
  3. Make sure these files are added to your target
  4. Import SFHFKeychainUtils.h where you want to use it

This is a little example on how to use this library.

// To store data
NSError *error = nil;
[SFHFKeychainUtils storeUsername:username andPassword:password forServiceName:kStoredData updateExisting:YES error:&error];

// To retrieve data
NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:kStoredData error:&error];

// To delete data from keychain
[SFHFKeychainUtils deleteItemForUsername:username andServiceName:kStoredData error:&error];
Parenteral answered 11/4, 2011 at 17:36 Comment(2)
What is kStoredData in this context? I'm getting a build error when trying to do this.Molloy
In this case kStoredData is a defined key... You can use any key... In this example, kStoreData is a key for a service name like @"com.company.app.serviceName". You can define a key like this in you .h file and then use it to store and retrieve your dataParenteral
C
3

The keychain is a total pain. You should use Buzz Andersen's STUtils library instead as a wrapper. It will make your life substantially easier. I've never had a problem with it.

Cleptomania answered 7/4, 2011 at 21:59 Comment(1)
This github project has now been deprecated; the author suggests github.com/ldandersen/STUtils/blob/master/Security as a replacement.Ralph
H
1

I had this problem also and solved it thanks to the accepter answer and the additional link to useyourloaf.

The problem I had was interesting, I needed to save only one value and decided to store it in the field kSecValueData. That is because I saw other posts about using the keychain and started my own implementation before turning to KeychainItemWrapper. This caused the following issue: On the first device I was testing (iPad 1st gen) I was getting an error in writeToKeychain. I changed device (also ipad 1st gen) and it worked! Back to the first device it still didn't work.

So I that point I knew that I had previously done something wrong in the device's keychain and couldn't revert it easily. The error codes I was getting were: -25300 on the writeToKeychain's SecItemCopyMatching (item not found) and right after -25299 on the SecItemAdd. (item duplicate)

With this question, this all made sense: the device has a key that matches any new key but the KeychainItemWrapper cannot delete it but the key cannot be retrieved. As soon as I added the same value to the field kSecAttrAccount, it started working.

Long story short, for other users having this problem, your problem might look different but pay attention to the details. If you have -25300 (item not found) followed by -25299 (item duplicate); make sure that you are setting a field that defines the uniqueness of your keychain item. If it doesn't work on one device, try another if you can you might be able to isolate the problem to one device. Apple keychain Error codes: http://developer.apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898-CH5g-CJBEABHG (search for Result Codes)

Henbit answered 10/7, 2013 at 16:59 Comment(1)
How can I find the item I have save previously? I have enumerated all items in this fashion #10967469, and there is none. Where is it hiding?Tupelo
H
1

For me, the solution was that I created a KeychainItemWrapper "singleton" and use that throughout the app. (Actually, in my case, I had a singleton dictionary full of KeychainItemWrapper-s, because I use more than one.)

This solved the problem where I was getting to a code path that effectively said "does this item exist on the keychain? No? Then add it. Whoops! NSAssert() that I'm trying to add an item that already exists (Error -25299)"

While I'm not certain, I suspect that the problem has to do with keychain syncing. I've had similar problems with NSUserDefaults, when I write to NSUD then, elsewhere in code, get the standardUserDefaults and read from them, and the update hasn't taken place yet (because I haven't done [ud synchronize], yet.)

In code, my routine looks like this:

+ (KeychainItemWrapper*) keyChainWrapperForKeyID: (NSString*) keyID
{
    static dispatch_once_t onceToken = 0;
    static NSMutableDictionary *rfcuKeyChains = nil;
    dispatch_once(&onceToken, ^{
        rfcuKeyChains = [NSMutableDictionary new];
    });

    KeychainItemWrapper *keychain = nil;
    @synchronized (rfcuKeyChains)
    {
        keychain = [rfcuKeyChains objectForKey: keyID];
        if (keychain == nil)
        {
            keychain = [[KeychainItemWrapper alloc] initWithIdentifier: keyID accessGroup: nil];
            [rfcuKeyChains setObject: keychain forKey: keyID];
        }
    }

    return keychain;
}

And I use it like this:

KeychainItemWrapper *keychain = [RFCUtils keyChainWrapperForKeyID: keyID];
NSString *firstLaunch = [keychain objectForKey: (__bridge id)(kSecAttrAccount)];
if (firstLaunch == nil)
{
    [keychain setObject: MY_APP_KEY forKey: (__bridge id)(kSecAttrAccount)];
}

(etc., similar calls in other places.)

Hautegaronne answered 21/5, 2015 at 14:16 Comment(0)
L
1

I tried all solutions listen above but nothing worked for me. It was only working on an actual device but not on the simulator.

My solution to run it on the simulator was to turn on "Share keychain entitlement".

Share Keychain entitlement

Luminous answered 10/10, 2016 at 20:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.