Error saving in the keychain with iphone sdk

守給你的承諾、 提交于 2019-11-29 20:11:22

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

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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!