in source/darwin/darwin_pki_utils.c [21:173]
int aws_import_public_and_private_keys_to_identity(
struct aws_allocator *alloc,
CFAllocatorRef cf_alloc,
const struct aws_byte_cursor *public_cert_chain,
const struct aws_byte_cursor *private_key,
CFArrayRef *identity,
const struct aws_string *keychain_path) {
int result = AWS_OP_ERR;
CFDataRef cert_data = CFDataCreate(cf_alloc, public_cert_chain->ptr, public_cert_chain->len);
CFDataRef key_data = CFDataCreate(cf_alloc, private_key->ptr, private_key->len);
if (!cert_data || !key_data) {
return aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
}
CFArrayRef cert_import_output = NULL;
CFArrayRef key_import_output = NULL;
SecExternalFormat format = kSecFormatUnknown;
SecExternalItemType item_type = kSecItemTypeCertificate;
SecItemImportExportKeyParameters import_params;
AWS_ZERO_STRUCT(import_params);
import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
import_params.passphrase = CFSTR("");
SecCertificateRef certificate_ref = NULL;
SecKeychainRef import_keychain = NULL;
if (keychain_path) {
OSStatus keychain_status = SecKeychainOpen(aws_string_c_str(keychain_path), &import_keychain);
if (keychain_status != errSecSuccess) {
AWS_LOGF_ERROR(
AWS_LS_IO_PKI,
"static: error opening keychain \"%s\" with OSStatus %d",
aws_string_c_str(keychain_path),
keychain_status);
return AWS_OP_ERR;
}
keychain_status = SecKeychainUnlock(import_keychain, 0, "", true);
if (keychain_status != errSecSuccess) {
AWS_LOGF_ERROR(
AWS_LS_IO_PKI,
"static: error unlocking keychain \"%s\" with OSStatus %d",
aws_string_c_str(keychain_path),
keychain_status);
return AWS_OP_ERR;
}
} else {
OSStatus keychain_status = SecKeychainCopyDefault(&import_keychain);
if (keychain_status != errSecSuccess) {
AWS_LOGF_ERROR(
AWS_LS_IO_PKI, "static: error opening the default keychain with OSStatus %d", keychain_status);
return AWS_OP_ERR;
}
}
aws_mutex_lock(&s_sec_mutex);
/* import certificate */
OSStatus cert_status =
SecItemImport(cert_data, NULL, &format, &item_type, 0, &import_params, import_keychain, &cert_import_output);
/* import private key */
item_type = kSecItemTypePrivateKey;
OSStatus key_status =
SecItemImport(key_data, NULL, &format, &item_type, 0, &import_params, import_keychain, &key_import_output);
CFRelease(cert_data);
CFRelease(key_data);
if (cert_status != errSecSuccess && cert_status != errSecDuplicateItem) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing certificate with OSStatus %d", (int)cert_status);
result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
goto done;
}
if (key_status != errSecSuccess && key_status != errSecDuplicateItem) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing private key with OSStatus %d", (int)key_status);
result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
goto done;
}
/* if it's already there, just convert this over to a cert and then let the keychain give it back to us. */
if (cert_status == errSecDuplicateItem) {
/* The text for this log is also in the README for each CRT and v2 IoT SDK. If changed, please also change
* where it is referenced. */
AWS_LOGF_INFO(
AWS_LS_IO_PKI,
"static: certificate has an existing certificate-key pair that was previously imported into the Keychain. "
"Using key from Keychain instead of the one provided.");
struct aws_array_list cert_chain_list;
if (aws_array_list_init_dynamic(&cert_chain_list, alloc, 2, sizeof(struct aws_byte_buf))) {
result = AWS_OP_ERR;
goto done;
}
if (aws_decode_pem_to_buffer_list(alloc, public_cert_chain, &cert_chain_list)) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: decoding certificate PEM failed.");
aws_array_list_clean_up(&cert_chain_list);
result = AWS_OP_ERR;
goto done;
}
struct aws_byte_buf *root_cert_ptr = NULL;
aws_array_list_get_at_ptr(&cert_chain_list, (void **)&root_cert_ptr, 0);
AWS_ASSERT(root_cert_ptr);
CFDataRef root_cert_data = CFDataCreate(cf_alloc, root_cert_ptr->buffer, root_cert_ptr->len);
if (root_cert_data) {
certificate_ref = SecCertificateCreateWithData(cf_alloc, root_cert_data);
CFRelease(root_cert_data);
}
aws_cert_chain_clean_up(&cert_chain_list);
aws_array_list_clean_up(&cert_chain_list);
} else {
certificate_ref = (SecCertificateRef)CFArrayGetValueAtIndex(cert_import_output, 0);
/* SecCertificateCreateWithData returns an object with +1 retain, so we need to match that behavior here */
CFRetain(certificate_ref);
}
/* if we got a cert one way or the other, create the identity and return it */
if (certificate_ref) {
SecIdentityRef identity_output;
OSStatus status = SecIdentityCreateWithCertificate(import_keychain, certificate_ref, &identity_output);
if (status == errSecSuccess) {
CFTypeRef certs[] = {identity_output};
*identity = CFArrayCreate(cf_alloc, (const void **)certs, 1L, &kCFTypeArrayCallBacks);
result = AWS_OP_SUCCESS;
goto done;
}
}
done:
aws_mutex_unlock(&s_sec_mutex);
if (certificate_ref) {
CFRelease(certificate_ref);
}
if (cert_import_output) {
CFRelease(cert_import_output);
}
if (key_import_output) {
CFRelease(key_import_output);
}
if (import_keychain) {
CFRelease(import_keychain);
}
return result;
}