Thanks again for the follow-up and for filing the bug. I don't think the keychain search list is getting messed up by my code. For completeness, here's the code I am using including a search for a private key after creating and deleting a new keychain to hold the imported credentials:
#import <Foundation/Foundation.h>
#import <Security/Security.h>
NSString *securityErrorMessageString(OSStatus status) { return (__bridge NSString *)SecCopyErrorMessageString(status, NULL); }
SecKeyRef privateKeyFromKeychain(NSString *keyName) {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassKey, (id)kSecClass,
(id)kSecAttrKeyClassPrivate, (id)kSecAttrKeyClass,
keyName, (id)kSecAttrLabel,
(id)kCFBooleanTrue, (id)kSecReturnRef,
nil];
SecKeyRef key = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&key);
if (status != errSecSuccess) {
NSLog(@"Could not get private key with name \"%@\": [%d] %@", keyName, (int)status, securityErrorMessageString(status));
if (key) CFRelease(key);
}
return key; //caller must use CFRelease on returned key
}
SecKeyRef privateKeyFromP12File(NSString *filePath, NSString *password, BOOL addIdentityToKeychain) {
SecKeyRef key = NULL;
NSString *p12Path = filePath.stringByExpandingTildeInPath;
if (!password || !password.length) {
NSLog(@"Password required for file: \"%@\".", p12Path);
return key;
}
NSData *p12Data = [NSData dataWithContentsOfFile:p12Path];
if (!p12Data) {
NSLog(@"Could not read p12 data from file: \"%@\".", p12Path);
return key;
}
OSStatus status;
SecKeychainRef keychain = NULL;
if (!addIdentityToKeychain) {
//SecPKCS12Import will automatically add the items to the keychain
//so create a new keychain for import then delete it
//make sure we create a unique keychain name:
NSString *temporaryDirectory = NSTemporaryDirectory();
NSString *keychainPath = [[temporaryDirectory stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]] stringByAppendingPathExtension:@"keychain"];
status = SecKeychainCreate(keychainPath.UTF8String, (UInt32)password.length, password.UTF8String, FALSE, NULL, &keychain);
if (status != errSecSuccess) {
if (keychain) {
SecKeychainDelete(keychain);
CFRelease(keychain);
}
NSLog(@"Could not create temporary keychain \"%@\": [%d] %@", keychainPath, (int)status, securityErrorMessageString(status));
return key;
}
}
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:password forKey:(id)kSecImportExportPassphrase];
if (!addIdentityToKeychain) [options setObject:(__bridge id)keychain forKey:(id)kSecImportExportKeychain];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
status = SecPKCS12Import((CFDataRef)p12Data, (CFDictionaryRef)options, &items);
if ((status == errSecSuccess) && (CFArrayGetCount(items) > 0)) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
status = SecIdentityCopyPrivateKey(identity, &key);
if (status != errSecSuccess) {
NSLog(@"Could not copy private key from P12 file \"%@\": [%d] %@", filePath, (int)status, securityErrorMessageString(status));
if (key) CFRelease(key);
}
} else if (status != errSecSuccess) {
NSLog(@"Could not import items from P12 file \"%@\": [%d] %@", filePath, (int)status, securityErrorMessageString(status));
}
if (keychain) {
SecKeychainDelete(keychain);
CFRelease(keychain);
}
CFRelease(items);
return key; //caller must use CFRelease on returned key
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *p12Path = @"path/to/file.p12";
NSString *password = @"superSecret123";
BOOL addIdentityToKeychain = NO;
SecKeyRef privateKey = privateKeyFromP12File(p12Path, password, addIdentityToKeychain);
NSLog(@"privateKeyFromP12File: %@", (__bridge id)privateKey);
if (privateKey) CFRelease(privateKey);
NSString *keyName = @"My Private Key";
privateKey = privateKeyFromKeychain(keyName);
NSLog(@"privateKeyFromKeychain: %@", (__bridge id)privateKey);
if (privateKey) CFRelease(privateKey);
}
return 0;
}
Regards, Jon