objective-ccryptographycore-foundationsecurity-framework

Exporting a Symmetric Key SecKeyRef item as a CFDataRef


I am having some trouble with getting a symmetric key exported from a SecKeyRef to a CFDataRef for wrapping or storing,or simply for use with other Cocoa code.

I have the following code:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    SecKeyRef sessionKey = [self generateRandomSymmetricKey];
    CFDataRef sessionKeyData = [self exportSymmetricKeyAsCFData:sessionKey];
}

- (SecKeyRef)generateRandomSymmetricKey {
    SecKeyRef cryptoKey = NULL;
    CFErrorRef error = NULL;

    // Create the dictionary of key parameters
    CFMutableDictionaryRef parameters = (__bridge CFMutableDictionaryRef)[NSMutableDictionary dictionaryWithObjectsAndKeys:kSecAttrKeyTypeAES, kSecAttrKeyType, (__bridge CFNumberRef)[NSNumber numberWithInt:256], kSecAttrKeySizeInBits, nil];

    // Generate a symmetric key based on the parameters
    cryptoKey = SecKeyGenerateSymmetric(parameters, &error);

    return cryptoKey;
}

- (CFDataRef)exportSymmetricKeyAsCFData:(SecKeyRef)cryptoKey {  
    // Create and populate the parameters object with a basic set of values
    SecItemImportExportKeyParameters params;

    params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
    params.flags = 0;
    params.passphrase = NULL;
    params.alertTitle = NULL;
    params.alertPrompt = NULL;
    params.accessRef = NULL;
    // These two values are for import
    params.keyUsage = NULL;
    params.keyAttributes = NULL;

    // Create and populate the key usage array
    CFMutableArrayRef keyUsage = (__bridge CFMutableArrayRef)[NSMutableArray arrayWithObjects:kSecAttrCanEncrypt, kSecAttrCanDecrypt, nil];

    // Create and populate the key attributes array
    CFMutableArrayRef keyAttributes = (__bridge CFMutableArrayRef)[NSMutableArray array];

    // Set the keyUsage and keyAttributes in the params object
    params.keyUsage = keyUsage;
    params.keyAttributes = keyAttributes;

    // Set the external format and flag values appropriately
    SecExternalFormat externalFormat = kSecFormatUnknown; // Should result in the default appropriate external format for the given key.
    int flags = 0;

    // Export the CFData Key
    CFDataRef keyData = NULL;
    CFShow(cryptoKey);
    OSStatus oserr = SecItemExport(cryptoKey, externalFormat, flags, &params, &keyData);
    if (oserr) {
        fprintf(stderr, "SecItemExport failed (oserr= %d)\n", oserr);
        exit(-1);
    }

    NSLog(@"Exported Symmetric Key Data: %@", [(__bridge NSData *)keyData bytes]);
    return keyData;
}

But all I end up with in my logs is:

SecItemExport failed (oserr= -25316)

"oserr= -25316" is defined in SecBase.h as:

errSecDataNotAvailable = -25316, /* The contents of this item cannot be retrieved. */

I would have originally thought that error to mean that the SecKeyRef was released early or something like that, but as you can see from the log, the SecKey is reporting itself just fine.

Any ideas as to what I'm missing or doing wrong here? Thanks!


Solution

  • After talking with Apple, it turns out this issue is caused by the "Extractable" attribute being set to NO, but having no way to set that parameter with SecKeyGenerateSymmetric()

    A bug has been filed, but essentially until that bug is resolved the solution is to use the depreciated method SecKeyGenerate().

    Instead of the - (SecKeyRef)generateRandomSymmetricKey method above use this:

    - (SecKeyRef)generateRandomSymmetricKey {
        SecKeyRef symmetricKey;
    
        OSStatus oserr = SecKeyGenerate(NULL, CSSM_ALGID_AES, 256, 0, (CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_ENCRYPT), CSSM_KEYATTR_EXTRACTABLE, NULL, &symmetricKey);
        if (oserr) {
            NSLog(@"SecKeyGenerate failed: %@", (__bridge NSString *)SecCopyErrorMessageString(oserr, NULL));
            exit(-1);
        }
    
        return symmetricKey;
    }
    

    I hope this helps someone.