11

I just found an interesting problem with my app. In the app I am saving the user's user name and password to the keychain.

keychainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyLoginPassword" accessGroup:nil];

[keychainWrapper setObject:usernameField.text forKey:(id)kSecAttrAccount];
[keychainWrapper setObject:passwordField.text forKey:(id)kSecValueData];

When this code is run in Debug it seems to work just fine. It updates each time and I can later retrieve the items from the keychain. When it is run in Distribution however the keychain never gets updated. I have verified that yes these lines of code are hit in both builds. I am using Xcode 4.2 with the iOS5 SDK and running the app on an iPad 2 with iOS5 installed.

iHorse
  • 491
  • 1
  • 5
  • 14

2 Answers2

17

I also had this problem, and it took me forever to figure out

There is a version of "KeychainWrapper" floating around that has it's SecItemUpdate within an NSAssert (among other things).

Whoever did this is a moron!, when building for release/distribution every NSAssert is nullified, meaning that code doesn't even get run.

For example:

NSAssert(SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck), @"Couldn't update the Keychain Item." );

Needs to become

OSStatus status = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck);
NSAssert(status == noErr, @"Couldn't update the Keychain Item." );

Notice how the actual SecItemUpdate is moved outside the NSAssert, and instead the result is checked

Important note: Attempting to update a value for kSecValueData, without also specifying a value for kSecAttrAccount, will cause the assertion to fail as well. So, if your intent is to store a single string of sensitive data (such as a list of credit card numbers), be sure to store some "account name" text in the kSecAttrAccount attribute, like so:

static NSString* kCardListXML = @"cardListXML";
static NSString* cardListAccountName = @"cardListAccount";

-(void)setCardListXML:(NSString*)xml {
  KeychainItemWrapper* wrapper =
    [[KeychainItemWrapper alloc] initWithIdentifier:kCardListXML accessGroup:nil];
  [wrapper setObject:cardListAccountName forKey:(id)CFBridgingRelease(kSecAttrAccount)];
  [wrapper setObject:xml forKey:(id)CFBridgingRelease(kSecValueData)];
}    

-(NSString*)getCardListXML {
  KeychainItemWrapper* wrapper =
    [[KeychainItemWrapper alloc] initWithIdentifier:kCardListXML accessGroup:nil];
  [wrapper setObject:cardListAccountName forKey:(id)CFBridgingRelease(kSecAttrAccount)];
  return [wrapper objectForKey:CFBridgingRelease(kSecValueData)];
}
Daniel Randall
  • 351
  • 2
  • 13
SomeGuy
  • 9,670
  • 3
  • 32
  • 35
16

When you include

keychainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyLoginPassword" accessGroup:nil];

[keychainWrapper setObject:usernameField.text forKey:(id)kSecAttrAccount];
[keychainWrapper setObject:passwordField.text forKey:(id)kSecValueData];

you also had to include

[keychainWrapper setObject:@"Myappstring" forKey: (id)kSecAttrService];

Or I get a "SIGABRT" error. (Myappstring) is a string defining your application.

Maybe I'm missing something, this should be done at least once.

Fahim Parkar
  • 30,974
  • 45
  • 160
  • 276
Andres Bucci
  • 947
  • 1
  • 9
  • 37
  • KeychainItemWrapper was mostly working for me, worked on many email addresses (stored as username in kSecAttrAccount), but one email in particular did not work. I then added the above line for kSecAttrService and all is now perfect! -Thank you @Andres – dbDev Feb 11 '13 at 21:22
  • I struggled with it for quite a while. Glad it helped – Andres Bucci Feb 12 '13 at 08:52
  • 2
    This fix worked for me. It's tricky to figure out what is going on from the error messages, because if you have ever previously set these values correctly, you will not see an error. Only when you change the initWithIdentifier: value AND fail to set kSecAttrService do you then start to get errors. – carbocation Feb 19 '14 at 00:51