cmacoscryptographysecurity-framework

Importing a PKCS12 Using SecItemImport


Apple's documentation for OS X talks about using SecItemImport to obtain a SecKeyRef. The function signature looks like this:

OSStatus SecItemImport (
   CFDataRef importedData,
   CFStringRef fileNameOrExtension,
   SecExternalFormat *inputFormat,
   SecExternalItemType *itemType,
   SecItemImportExportFlags flags,
   const SecItemImportExportKeyParameters *keyParams,
   SecKeychainRef importKeychain,
   CFArrayRef *outItems
);

The following code will attempt to load a PKCS12 byte array that contains a single RSA key:

#include <Security/Security.h>
#include <Security/SecKey.h>
#include <CoreFoundation/CoreFoundation.h>

int main(void) {
    CFArrayRef array = NULL;
    SecItemImportExportKeyParameters params;
    SecExternalItemType itemType = kSecItemTypeUnknown;
    SecExternalFormat format = kSecFormatUnknown;
    params.flags = kSecKeyNoAccessControl;
    UInt8 bytes[] = {
        0x30, 0x82, 0x2, 0x1e, 0x2, 0x1, 0x3, 0x30, 0x82, 0x1, 0xe8, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7,
        0xd, 0x1, 0x7, 0x1, 0xa0, 0x82, 0x1, 0xd9, 0x4, 0x82, 0x1, 0xd5, 0x30, 0x82, 0x1, 0xd1, 0x30, 0x82,
        0x1, 0xcd, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x7, 0x1, 0xa0, 0x82, 0x1, 0xbe, 0x4,
        0x82, 0x1, 0xba, 0x30, 0x82, 0x1, 0xb6, 0x30, 0x82, 0x1, 0xb2, 0x6, 0xb, 0x2a, 0x86, 0x48, 0x86,
        0xf7, 0xd, 0x1, 0xc, 0xa, 0x1, 0x2, 0xa0, 0x82, 0x1, 0x86, 0x30, 0x82, 0x1, 0x82, 0x30, 0x1c, 0x6,
        0xa, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0xc, 0x1, 0x3, 0x30, 0xe, 0x4, 0x8, 0x8f, 0x14, 0x15,
        0x85, 0x8e, 0x1c, 0x64, 0x6c, 0x2, 0x2, 0x8, 0x0, 0x4, 0x82, 0x1, 0x60, 0x86, 0x44, 0x55, 0x7e, 0xa3,
        0xce, 0x63, 0xb0, 0x83, 0xf6, 0x34, 0xb8, 0x88, 0xe3, 0x6e, 0xbf, 0x7e, 0xcb, 0xe7, 0x4f, 0x57, 0xde,
        0xaa, 0x4f, 0x28, 0xaa, 0x38, 0x6f, 0xc0, 0xd2, 0xcc, 0xc9, 0x95, 0x1b, 0x69, 0x2c, 0x7b, 0x78, 0xdf,
        0x77, 0xd7, 0xb3, 0x30, 0xe6, 0x3f, 0x6f, 0xaa, 0xe2, 0x68, 0x2e, 0x50, 0x28, 0x97, 0x16, 0x1d, 0xb8,
        0x7a, 0x26, 0x80, 0x57, 0x15, 0xf5, 0xe5, 0xce, 0x45, 0xed, 0x99, 0x68, 0x4f, 0x87, 0x97, 0x7c, 0xa0,
        0x19, 0xaf, 0x41, 0xf4, 0xcb, 0xdb, 0x2b, 0xd5, 0x42, 0xac, 0x9c, 0x50, 0x63, 0xd5, 0x1f, 0x5c, 0x1b,
        0x32, 0x1d, 0x3a, 0x77, 0x14, 0x78, 0x97, 0xe4, 0x38, 0xc2, 0x18, 0x9b, 0x5f, 0xa4, 0xf1, 0xec, 0xe0,
        0xd3, 0xc4, 0x7d, 0xcb, 0x25, 0x7, 0xd4, 0x68, 0x8b, 0x6d, 0x1d, 0x68, 0xa6, 0x8d, 0xf7, 0xf3, 0x63,
        0xf6, 0x6f, 0x5c, 0x8e, 0xed, 0x13, 0xdd, 0x11, 0xd7, 0x9, 0xe8, 0x6b, 0xda, 0x12, 0x44, 0x75, 0x15,
        0x82, 0xfa, 0xf6, 0xa1, 0x92, 0x54, 0x81, 0x9a, 0xa8, 0x4b, 0x21, 0x69, 0x8c, 0xc7, 0xba, 0x3e, 0x59,
        0xb6, 0x5b, 0x3, 0x50, 0xad, 0xd5, 0x6e, 0x9e, 0x11, 0x85, 0x12, 0x28, 0x6b, 0xf6, 0x4e, 0xbe, 0xaf,
        0xac, 0x9b, 0xee, 0x41, 0xf1, 0x3c, 0x36, 0xd9, 0xed, 0x4b, 0x68, 0x5e, 0x17, 0x88, 0xe5, 0xc2, 0x51,
        0x25, 0x19, 0xa4, 0xda, 0x1b, 0xb8, 0xec, 0xe2, 0x16, 0x42, 0xb1, 0x43, 0x32, 0x83, 0x89, 0x7a, 0xf3,
        0x3e, 0xee, 0x45, 0x18, 0x98, 0xb7, 0xe0, 0x23, 0xe4, 0x59, 0x58, 0x89, 0xa7, 0xf3, 0x50, 0x43, 0x3e,
        0xa7, 0xf9, 0xf, 0x89, 0x6c, 0xd1, 0xb3, 0xd7, 0xc7, 0x61, 0xa9, 0x75, 0x72, 0x52, 0x8a, 0xc0, 0x17,
        0x60, 0xbc, 0x26, 0x51, 0xec, 0x0, 0x46, 0xdb, 0x98, 0x41, 0xa0, 0x18, 0x7b, 0x2f, 0x11, 0xde, 0xb5,
        0xdc, 0xa4, 0x63, 0xbc, 0x93, 0x93, 0xcc, 0x98, 0x90, 0x74, 0xcb, 0xf6, 0xa, 0xe5, 0x99, 0x66, 0xd6,
        0x40, 0x81, 0x3, 0x7a, 0x40, 0x8, 0xa9, 0x42, 0x6, 0x53, 0xbe, 0x2c, 0x3a, 0x82, 0xe2, 0x9a, 0x62, 0x5a,
        0x1b, 0xb6, 0x3f, 0xd7, 0x10, 0x7f, 0x57, 0x0, 0xdb, 0x29, 0x8d, 0x4d, 0xe8, 0x9c, 0x70, 0x86, 0x66,
        0x78, 0x7b, 0x96, 0xba, 0xef, 0xb4, 0x4c, 0x30, 0x20, 0x97, 0x25, 0x5d, 0xf6, 0xff, 0x9d, 0x97, 0x60,
        0x34, 0x58, 0x2e, 0x6a, 0xf7, 0x12, 0x9f, 0x3b, 0x77, 0x2e, 0x3e, 0x43, 0x75, 0x97, 0x73, 0xf6, 0x53,
        0x0, 0x9b, 0x74, 0x34, 0xf4, 0xec, 0x31, 0x19, 0x30, 0x17, 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7,
        0xd, 0x1, 0x9, 0x14, 0x31, 0xa, 0x1e, 0x8, 0x0, 0x6e, 0x0, 0x61, 0x0, 0x6d, 0x0, 0x65, 0x30, 0x2d,
        0x30, 0x21, 0x30, 0x9, 0x6, 0x5, 0x2b, 0xe, 0x3, 0x2, 0x1a, 0x5, 0x0, 0x4, 0x14, 0x95, 0x9, 0x91,
        0xf7, 0x6c, 0x7f, 0x35, 0x99, 0x64, 0xfc, 0x3e, 0x5e, 0xcf, 0xb6, 0x24, 0x8a, 0xc, 0x54, 0x44, 0xc6,
        0x4, 0x8, 0xfe, 0xe0, 0xe7, 0xbb, 0x72, 0xe1, 0xc9, 0x97
    };
    FILE* file = fopen( "test.p12", "wb" );
    fwrite( bytes, sizeof(UInt8), sizeof(bytes)/sizeof(UInt8), file );

    params.passphrase = CFStringCreateWithCString(kCFAllocatorDefault, "pass", kCFStringEncodingASCII);

    CFDataRef dataref = CFDataCreate(kCFAllocatorDefault, bytes, sizeof(bytes)/sizeof(bytes[0]));

    OSStatus res = SecItemImport(dataref, CFSTR(".p12"), &format, &itemType, 0, &params, NULL, &array);
    printf("response: %d\n", res);
    printf("format: %d\n", format);
    printf("itemType: %d\n", itemType);
    printf("count: %ld\n", CFArrayGetCount(array));
}

To compile the above you can use clang filename.c -framework Security -framework CoreFoundation.

This code returns no error (OSStatus 0) but fails to populate the array with a parsed SecKeyRef. I've tried quite a few different flags but haven't been able to get it to return the data needed. Does anyone know what I'm doing wrong?


Solution

  • This really looks like a Keychain bug. That isn't shocking; Keychain has lots of little corner cases that aren't well handled. I'd open a radar (bugreport.apple.com). Some additional notes:

    PKCS12 is usually used to store a certificate along with a private key. You're just importing a bare private key. I'm betting that's not well tested, although it claims to be supported:

    Note: When importing a PKCS12 blob, typically one SecIdentityRef object and zero or more additional SecCertificateRef objects are returned in outItems. No SecKeyRef objects are returned unless a key is found in the incoming blob that does not have a matching certificate.

    I'd be curious if this would work if there were a certificate involved.

    I've tried this code with a keychain (created with SecKeychainCreate). The resulting keychain is readable by Keychain Access and contains the private key, so the import is working.

    I've tried using all the same code as SecPKCS12Import, but it returns the same result. For instance, I made sure to memset() the parameters structure, which your code should, but doesn't.

    As an irrelevant point, you're leaking the passphrase. You should use CFSTR() here to pass the constant string.