Objective C: Unable to fetch SecKeyRef from PEM private key Objective C: Unable to fetch SecKeyRef from PEM private key objective-c objective-c

Objective C: Unable to fetch SecKeyRef from PEM private key


Unfortunately the Security framework on iOS requires that private keys be in the PKCS12 format, with a passphrase. Public keys can be in X509 armored DER or PKCS12, but private keys are required to be PKCS12. The private key you are trying to use is a PEM-formatted RSA key.

If you have access to the key it can be converted using the openssl command line tools:

openssl pkcs12 -export -nocerts -inkey privatekey.pem -out privatekey.p12

This will create a PKCS12 file with the private key, and requires a passphrase. If you do not have control of the private key (for example, if it is coming from an external source such as a server), you are out of luck.

But let's assume you were able to do the steps above to convert that troubesome PEM RSA private key to PKCS12.Extracting the private key from the PKCS12 data is not too difficult:

  1. Load the PKCS12 as NSData. You can do this using dataWithContentsOfURL: if this is a resource on the filesystem.
  2. Use SecPKCS12Import to import the PKCS12 data with the passphrase.
  3. Extract the SecIdentityRef from the imported items.
  4. Copy the private key from the SecIdentityRef

A function for doing so would be:

OSStatus    SecKeyPrivatePKCS12Import(CFDataRef keyData, CFStringRef passphrase, SecKeyRef *privateKey){    OSStatus        status              = errSecSuccess;    CFDictionaryRef secImportOptions    = NULL;    CFArrayRef      secImportItems      = NULL;    if ((keyData != NULL) && (CFStringGetLength(passphrase) > 0) ){        const void *keys[] = { kSecImportExportPassphrase };        const void *values[] = { passphrase };        secImportOptions = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);        status = SecPKCS12Import((CFDataRef) keyData, (CFDictionaryRef)secImportOptions, &secImportItems);        if (CFArrayGetCount(secImportItems) > 0){            CFDictionaryRef identityDict = CFArrayGetValueAtIndex(secImportItems, 0);            SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);            SecIdentityCopyPrivateKey(identityApp, privateKey);        }    }    return status;}

Calling it from Objective-C would look like:

OSStatus status = errSecSuccess;status = SecKeyPrivatePKCS12Import((_bridge CFDataRef)data, (_bridge CFStringRef)passphrase, &privateKey);if (privateKey == NULL){   // Check the status value for why it failed}

Assuming that "data" is an instance of NSData that contains the PKCS12 data, and "passphrase" is an NSString instance representing the passphrase. On success "privateKey" is populated with the private key imported from the PKCS12 data.


I have faced the same issue when I was working with java server and iPhone application and my work around was as below.

  1. Generate p12 on java server. [Remember to note down the password.]
  2. Convert raw bytes of p12 file into base 64 string.
  3. Send those data to iOS application no matter how you want.

    3.1 You can put base 64 in text file and send that to iOS. [Safest way and working fine in my case.]

    3.2 You can use JSON string to send that string. [This might corrupt your data.]

  4. Once you get the data on iPhone application convert base 64 string to NSData. NSData+Base64
  5. Use following method to get the SecKeyRef of your private key.

    - (SecKeyRef)getPrivateKeyFromData:(NSData *)p12Data withPassword:(NSString *)password {    NSMutableDictionary *options = [[NSMutableDictionary alloc] init];    SecKeyRef privateKey = NULL;    [options setObject:password forKey:(__bridge id)kSecImportExportPassphrase];    CFArrayRef items = NULL;// = CFArrayCreate(NULL, 0, 0, NULL);    OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)p12Data,                                          (__bridge CFDictionaryRef)options, &items);    if (securityError == noErr && CFArrayGetCount(items) > 0) {         CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);         SecIdentityRef identityApp =         (SecIdentityRef)CFDictionaryGetValue(identityDict,                                      kSecImportItemIdentity);         securityError = SecIdentityCopyPrivateKey(identityApp, &privateKey);    if (securityError != noErr) {            privateKey = NULL;        }    }    //NSLog(@"-------------------- Private Key Error %d",(int)securityError);    CFRelease(items);    options = nil;    p12Data = nil;    password = nil;    return privateKey;}

Hope this helps!!!!!


You have store your private key and the certificate in the keychain. Otherwise SecItemCopyMatching will not do anything. You only have to import this once.

/* importing client identity (private key) */NSData* certificateData = ... ; // decoded pkcs21 certificate from base64 pemNSString* passcode = @"passphrased used to encrypt the private key";CFDictionaryRef optionsDictionary = (__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: passcode,  kSecImportExportPassphrase, nil];CFArrayRef certificates;OSStatus error = SecPKCS12Import((__bridge CFDataRef) certificateData, optionsDictionary, &certificates);CFDictionaryRef myIDs = CFArrayGetValueAtIndex(certificates, 0); SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(myIDs, kSecImportItemIdentity);NSDictionary* clientCertificateQuery = @{(__bridge id)kSecValueRef        : identity,                                         (__bridge id)kSecAttrLabel       : @"some label you can use to find the item again with SecItemCopyMatching"};OSStatus err = SecItemAdd((__bridge CFDictionaryRef) clientCertificateQuery, NULL);

Then you can later use SecItemCopyMatching to get the identity and SecIdentityCopyPrivateKey to get the private key.

NSDictionary* clientCertificateQuery = @{(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll,                                         (__bridge id)kSecClass      : (__bridge id)kSecClassIdentity,                                         (__bridge id)kSecReturnRef   : (__bridge id)kCFBooleanTrue};SecIdentityRef identity = NULL;OSStatus errorCode = SecItemCopyMatching((__bridge CFDictionaryRef) clientCertificateQuery, &identity);SecKeyRef privateKeyRef;OSStatus err = SecIdentityCopyPrivateKey (identity, &privateKeyRef);

Always check the OSStatus errors as you will definitely run into errSecDuplicateItem.

Be sure to read Apple's Certificate, Key, and Trust Services Reference.