Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SecItem for macOS and iOS #646

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions include/aws/io/private/pki_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifdef AWS_OS_APPLE
/* It's ok to include external headers because this is a PRIVATE header file */
# include <CoreFoundation/CFArray.h>
# include <Security/Security.h>
#endif /* AWS_OS_APPLE */

struct aws_string;
Expand All @@ -30,6 +31,21 @@ AWS_IO_API const char *aws_determine_default_pki_ca_file(void);

#ifdef AWS_OS_APPLE
# if !defined(AWS_OS_IOS)
/**
* Imports a PEM armored PKCS#7 public/private key pair
* into identity for use with SecurityFramework.
*
* WIP SecItem
*/
int aws_import_public_and_private_keys_to_keychain(
struct aws_allocator *alloc,
CFAllocatorRef cf_alloc,
const struct aws_byte_cursor *public_cert_chain,
const struct aws_byte_cursor *private_key,
SecCertificateRef *sec_certificate_ref,
CFStringRef *cert_label,
CFStringRef *key_label);

/**
* Imports a PEM armored PKCS#7 public/private key pair
* into identity for use with SecurityFramework.
Expand Down
5 changes: 5 additions & 0 deletions include/aws/io/tls_channel_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ struct aws_tls_ctx_options {
*/
struct aws_byte_buf pkcs12_password;

// TODO STEVE
// We probably want to set whatever is needed for SecItem support in this struct somewhere.
const char *cert_label;
const char *key_label;

# if !defined(AWS_OS_IOS)
/**
* On Apple OS you can also use a custom keychain instead of
Expand Down
167 changes: 167 additions & 0 deletions source/darwin/darwin_pki_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,173 @@ int aws_import_ecc_key_into_keychain(
return result;
}

int aws_import_public_and_private_keys_to_keychain(
struct aws_allocator *alloc,
CFAllocatorRef cf_alloc,
const struct aws_byte_cursor *public_cert_chain,
const struct aws_byte_cursor *private_key,
SecCertificateRef *sec_certificate_ref,
CFStringRef *cert_label,
CFStringRef *key_label) {

AWS_PRECONDITION(public_cert_chain != NULL);
AWS_PRECONDITION(private_key != NULL);

int result = AWS_OP_ERR;

CFDataRef cert_data_ref = NULL;
CFDataRef key_data_ref = NULL;

CFDictionaryRef cert_dict = NULL;
CFDictionaryRef key_dict = NULL;

CFDictionaryRef update_query_cert_dict = NULL;
CFDictionaryRef update_cert_dict = NULL;

CFDictionaryRef update_key_dict = NULL;

cert_data_ref = CFDataCreate(cf_alloc, public_cert_chain->ptr, public_cert_chain->len);
if (!cert_data_ref) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating public cert chain data.");
result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

key_data_ref = CFDataCreate(cf_alloc, private_key->ptr, private_key->len);
if (!key_data_ref) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating private key data.");
result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

// Create dictionary for certificate
const void *cert_keys[] = { kSecClass, kSecAttrLabel, kSecValueData };
const void *cert_values[] = { kSecClassCertificate, cert_label, cert_data_ref };
cert_dict = CFDictionaryCreate(
cf_alloc,
cert_keys,
cert_values,
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Create dictionary for private key
const void *key_keys[] = { kSecClass, kSecAttrLabel, kSecValueData };
const void *key_values[] = { kSecClassKey, key_label, key_data_ref };
key_dict = CFDictionaryCreate(
cf_alloc,
key_keys,
key_values,
3,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

aws_mutex_lock(&s_sec_mutex);

OSStatus cert_status = SecItemAdd(cert_dict, NULL);
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "DEBUG: after SecItemAdd OSStatus %d", (int)cert_status);
if (cert_status == errSecDuplicateItem) {
AWS_LOGF_INFO(
AWS_LS_IO_PKI,
"static: certificate has an existing label-value pair that was previously imported into the Keychain. "
"Updating value in the keychain to the one provided.");
const void *update_cert_query_keys[] = { kSecClass, kSecAttrLabel };
const void *update_cert_query_values[] = { kSecClassCertificate, cert_label };
update_query_cert_dict = CFDictionaryCreate(
cf_alloc,
update_cert_query_keys,
update_cert_query_values,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

// Create update dictionary with the new value
const void *update_cert_keys[] = { kSecValueData };
const void *update_cert_values[] = { cert_data_ref };
update_cert_dict = CFDictionaryCreate(
cf_alloc,
update_cert_keys,
update_cert_values,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

cert_status = SecItemUpdate(update_query_cert_dict, update_cert_dict);
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "DEBUG: after SecItemUpdate OSStatus %d", (int)cert_status);
}

if (cert_status != errSecSuccess) {
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;
}

OSStatus key_status = SecItemAdd(key_dict, NULL);
if (key_status == errSecDuplicateItem) {
AWS_LOGF_INFO(
AWS_LS_IO_PKI,
"static: private key has an existing key-value pair that was previously imported into the Keychain. "
"Updating value in the keychain to the one provided.");
const void *update_key_keys[] = { kSecValueData };
const void *update_key_values[] = { key_data_ref };
update_key_dict = CFDictionaryCreate(
cf_alloc,
update_key_keys,
update_key_values,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
key_status = SecItemUpdate(key_dict, update_key_dict);
}

if (key_status != errSecSuccess) {
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;
}

OSStatus copy_status = SecItemCopyMatching(cert_dict, (CFTypeRef *)sec_certificate_ref);

if(copy_status != errSecSuccess){
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed retrieiving stored certificate.");
result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

result = AWS_OP_SUCCESS;

done:
aws_mutex_unlock(&s_sec_mutex);

if (cert_data_ref) {
CFRelease(cert_data_ref);
}
if (key_data_ref) {
CFRelease(key_data_ref);
}

if (cert_dict) {
CFRelease(cert_dict);
}

if (key_dict) {
CFRelease(key_dict);
}

if (update_query_cert_dict) {
CFRelease(update_query_cert_dict);
}
if (update_cert_dict) {
CFRelease(update_cert_dict);
}

if (update_key_dict) {
CFRelease(update_key_dict);
}

return result;
}

int aws_import_public_and_private_keys_to_identity(
struct aws_allocator *alloc,
CFAllocatorRef cf_alloc,
Expand Down
68 changes: 65 additions & 3 deletions source/darwin/secure_transport_tls_channel_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,9 @@ struct secure_transport_ctx {
CFAllocatorRef wrapped_allocator;
CFArrayRef certs;
CFArrayRef ca_cert;
SecCertificateRef sec_certificate_ref;
CFStringRef cert_label;
CFStringRef key_label;
enum aws_tls_versions minimum_version;
struct aws_string *alpn_list;
bool veriify_peer;
Expand All @@ -799,9 +802,6 @@ static struct aws_channel_handler *s_tls_handler_new(

struct secure_transport_handler *secure_transport_handler =
(struct secure_transport_handler *)aws_mem_calloc(allocator, 1, sizeof(struct secure_transport_handler));
if (!secure_transport_handler) {
return NULL;
}

secure_transport_handler->handler.alloc = allocator;
secure_transport_handler->handler.impl = secure_transport_handler;
Expand Down Expand Up @@ -885,9 +885,13 @@ static struct aws_channel_handler *s_tls_handler_new(
SSLSetSessionOption(secure_transport_handler->ctx, kSSLSessionOptionBreakOnServerAuth, true);
}

// STEVE DEBUG Setup for use
if (secure_transport_ctx->certs) {
status = SSLSetCertificate(secure_transport_handler->ctx, secure_transport_ctx->certs);
}
// if (secure_transport_ctx->sec_certificate_ref) {
// status = SSLSetCertificate(secure_transport_handler->ctx, secure_transport_ctx->sec_certificate_ref);
// }

secure_transport_handler->ca_certs = NULL;
if (secure_transport_ctx->ca_cert) {
Expand Down Expand Up @@ -968,6 +972,18 @@ static void s_aws_secure_transport_ctx_destroy(struct secure_transport_ctx *secu
aws_release_certificates(secure_transport_ctx->ca_cert);
}

if (secure_transport_ctx->sec_certificate_ref) {
CFRelease(secure_transport_ctx->sec_certificate_ref);
}

if (secure_transport_ctx->cert_label) {
CFRelease(secure_transport_ctx->cert_label);
}

if (secure_transport_ctx->key_label) {
CFRelease(secure_transport_ctx->key_label);
}

if (secure_transport_ctx->alpn_list) {
aws_string_destroy(secure_transport_ctx->alpn_list);
}
Expand Down Expand Up @@ -1006,6 +1022,9 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru
secure_transport_ctx->veriify_peer = options->verify_peer;
secure_transport_ctx->ca_cert = NULL;
secure_transport_ctx->certs = NULL;
secure_transport_ctx->sec_certificate_ref = NULL;
secure_transport_ctx->cert_label = NULL;
secure_transport_ctx->key_label = NULL;
secure_transport_ctx->ctx.alloc = alloc;
secure_transport_ctx->ctx.impl = secure_transport_ctx;
aws_ref_count_init(
Expand All @@ -1031,6 +1050,34 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru

struct aws_byte_cursor cert_chain_cur = aws_byte_cursor_from_buf(&options->certificate);
struct aws_byte_cursor private_key_cur = aws_byte_cursor_from_buf(&options->private_key);

// Steve TODO convert key and cert labels and store them in secure_transport_ctx to use for
// storage and recovery.
if (options->cert_label) {
secure_transport_ctx->cert_label = CFStringCreateWithCString(
secure_transport_ctx->wrapped_allocator,
options->cert_label,
kCFStringEncodingUTF8);
} else {
// apply a default label to certificate
secure_transport_ctx->cert_label = CFStringCreateWithCString(
secure_transport_ctx->wrapped_allocator,
"default_cert_label",
kCFStringEncodingUTF8);
}
if (options->key_label) {
secure_transport_ctx->key_label = CFStringCreateWithCString(
secure_transport_ctx->wrapped_allocator,
options->key_label,
kCFStringEncodingUTF8);
} else {
// apply a default label to certificate
secure_transport_ctx->key_label = CFStringCreateWithCString(
secure_transport_ctx->wrapped_allocator,
"default_key_label",
kCFStringEncodingUTF8);
}
// STEVE DEBUG
if (aws_import_public_and_private_keys_to_identity(
alloc,
secure_transport_ctx->wrapped_allocator,
Expand All @@ -1042,6 +1089,21 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru
AWS_LS_IO_TLS, "static: failed to import certificate and private key with error %d.", aws_last_error());
goto cleanup_wrapped_allocator;
}

// if (aws_import_public_and_private_keys_to_keychain(
// alloc,
// secure_transport_ctx->wrapped_allocator,
// &cert_chain_cur,
// &private_key_cur,
// &secure_transport_ctx->sec_certificate_ref,
// secure_transport_ctx->cert_label,
// secure_transport_ctx->key_label)){
// AWS_LOGF_ERROR(
// AWS_LS_IO_TLS,
// "static: failed to import certificate and private key with error %d.",
// aws_last_error());
// goto cleanup_wrapped_allocator;
// }
#endif
} else if (aws_tls_options_buf_is_set(&options->pkcs12)) {
AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: a pkcs$12 certificate and key has been set, setting it up now.");
Expand Down
Loading