11

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?

Alex Gaynor
  • 14,353
  • 9
  • 63
  • 113
Paul Kehrer
  • 13,466
  • 4
  • 40
  • 57
  • Don't you need to flush/close the file after writing to it? Was the file created correctly? – Maarten Bodewes May 12 '14 at 22:36
  • The file isn't relevant. It's just for testing. Note how it isn't read later? The file also seems correct (in that openssl can read it fine). – Rob Napier May 13 '14 at 00:10
  • @RobNapier OK, I can see that now, unfortunately my Octational tool is at work. Paul, have you tried `kSecFormatPKCS12` as format? I cannot easily check the validity of the P12 right now, the hex is in a bit of a tricky format. – Maarten Bodewes May 13 '14 at 00:19
  • Looks like PKCS#7 (CMS) instead of PKCS#12 to me. Sheesh, wrote a hex decoder just to see that :) It contains a PKCS#12 though. – Maarten Bodewes May 13 '14 at 00:33
  • Oops, indeed that `FILE *` existed only for test. I intended to strip it out for the example, but failed. @owlstead, I will take a look at that. The PKCS12 was generated via OpenSSL using the export flags, but I'll try your alternate hex. – Paul Kehrer May 13 '14 at 03:06

1 Answers1

4

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.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    If I import a key+cert pair then I do get an element in the CFArray so it looks like that is a potential workaround. Ultimately the goal here is to import an RSA/DSA/EC key for use with Apple's signing APIs directly so I may experiment with some other import formats (PKCS1/PKCS8 would be ideal). Thanks! – Paul Kehrer May 13 '14 at 03:28
  • @PaulKehrer Did you have any luck? I stuck in a similar problem… Thank you! – Frizlab Feb 07 '17 at 21:20