开发者

Error saving in the keychain with iphone sdk

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:(NSS开发者_开发百科tring *)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"}
}


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];   


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).


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];


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.


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.)


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


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)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜