6
votes

The KeychainItemWrapper class in the Apple GenericKeychain sample use kSecValueData key to store password.

But the reference http://developer.apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898

says kSecValueData is used in the results dictionary for SecItemCopyMatching or SecItemAdd, indicating the type of values returned.

which key should I use when I call SecItemAdd to create a keychain item?

1

1 Answers

7
votes

You should use kSecValue data as the key to store the password (in NSData or CFDataRef format).

The reference is a bit unclear in this subject, kSecValueData key works as output key as well as input key. That is, you use it when you query a keychain item (SecItemCopyMatching) and specify a kSecReturnAttributes key, so the result is returned as a dictionary, the password will get stored under a kSecValueData key of that dictionary. And you also use it when you add an item to the keychain (SecItemAdd), storing the NSData or CFDataRef value of your password in the kSecValueData key before calling the method.

Here's an example of both cases:

Retrieving password:

NSMutableDictionary *queryDictionary = [[NSMutableDictionary alloc] init];
[queryDictionary setObject: (__bridge id)kSecClassGenericPassword forKey: (__bridge id<NSCopying>)kSecClass];
[queryDictionary setObject:service forKey:kSecAttrService];
[queryDictionary setObject:account forKey:kSecAttrAccount];
// The result will be a dictionary containing the password attributes...
[queryDictionary setObject:YES forKey:(__bridge id<NSCopying>)(kSecReturnAttributes)];
// ...one of those attributes will be a kSecValueData with the password
[queryDictionary setObject:YES forKey:(__bridge id<NSCopying>)(kSecReturnData)];
OSStatus sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)(queryDictionary), (CFTypeRef *)&result);
if (sanityCheck != noErr)
{
    NSDictionary * resultDict = (__bridge NSDictionary *)result;
    // here's the queried password value
    NSData *passwordValue = [resultDict objectForKey:(__bridge id)(kSecValueData)];
}

Adding password:

NSString *passwordString = @"my password value";
NSData *passwordData = [passwordString dataUsingEncoding:NSUTF8StringEncoding];
CFDictionaryRef result = nil;
NSMutableDictionary *addDictionary = [[NSMutableDictionary alloc] init];
[addDictionary setObject: (__bridge id)kSecClassGenericPassword forKey: (__bridge id<NSCopying>)kSecClass];
[addDictionary setObject:service forKey:kSecAttrService];
[addDictionary setObject:account forKey:kSecAttrAccount];

// here goes the password value
[addDictionary setObject:passwordData forKey:(__bridge id<NSCopying>)(kSecValueData)];

OSStatus sanityCheck = SecItemAdd((__bridge CFDictionaryRef)(queryDictionary), NULL)
if (sanityCheck != noErr)
{
   // if no error the password got successfully stored in the keychain
}