/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include "crt.h" #include "java_class_ids.h" #include "tls_context_pkcs11_options.h" #include "custom_key_op_handler.h" /* Have to wrap the native struct so we can manage string lifetime */ struct jni_tls_ctx_options { /* Must be first thing in the structure so casts to aws_tls_ctx_options work */ struct aws_tls_ctx_options options; /* these strings get copied from java, so we don't have to pin and track references */ struct aws_string *ca_file; struct aws_string *ca_path; struct aws_string *alpn_list; struct aws_string *certificate_path; struct aws_string *private_key_path; struct aws_string *pkcs12_path; struct aws_string *pkcs12_password; struct aws_string *certificate; struct aws_string *private_key; struct aws_string *windows_cert_store_path; struct aws_string *ca_root; struct aws_tls_ctx_pkcs11_options *pkcs11_options; struct aws_custom_key_op_handler *custom_key_op_handler; }; /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */ #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */ # else # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpointer-to-int-cast" # pragma GCC diagnostic ignored "-Wint-to-pointer-cast" # endif #endif static void s_jni_tls_ctx_options_destroy(struct jni_tls_ctx_options *tls) { if (tls == NULL) { return; } aws_string_destroy(tls->ca_file); aws_string_destroy(tls->ca_path); aws_string_destroy(tls->alpn_list); aws_string_destroy(tls->certificate_path); aws_string_destroy(tls->private_key_path); aws_string_destroy(tls->pkcs12_path); aws_string_destroy_secure(tls->pkcs12_password); aws_string_destroy(tls->certificate); aws_string_destroy_secure(tls->private_key); aws_string_destroy(tls->windows_cert_store_path); aws_string_destroy(tls->ca_root); aws_tls_ctx_pkcs11_options_from_java_destroy(tls->pkcs11_options); aws_custom_key_op_handler_java_release(tls->custom_key_op_handler); aws_tls_ctx_options_clean_up(&tls->options); struct aws_allocator *allocator = aws_jni_get_allocator(); aws_mem_release(allocator, tls); } JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsNew( JNIEnv *env, jclass jni_class, jint jni_min_tls_version, jint jni_cipher_pref, jstring jni_alpn, jstring jni_certificate, jstring jni_private_key, jstring jni_cert_path, jstring jni_key_path, jstring jni_ca, jstring jni_ca_filepath, jstring jni_ca_dirpath, jboolean jni_verify_peer, jstring jni_pkcs12_path, jstring jni_pkcs12_password, jobject jni_pkcs11_options, jobject jni_custom_key_op, jstring jni_windows_cert_store_path) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct jni_tls_ctx_options *tls = aws_mem_calloc(allocator, 1, sizeof(struct jni_tls_ctx_options)); AWS_FATAL_ASSERT(tls); aws_tls_ctx_options_init_default_client(&tls->options, allocator); /* Certs or paths will cause an init, which overwrites other fields, so do those first */ if (jni_certificate && jni_private_key) { tls->certificate = aws_jni_new_string_from_jstring(env, jni_certificate); if (!tls->certificate) { aws_jni_throw_runtime_exception(env, "failed to get certificate string"); goto on_error; } tls->private_key = aws_jni_new_string_from_jstring(env, jni_private_key); if (!tls->private_key) { aws_jni_throw_runtime_exception(env, "failed to get privateKey string"); goto on_error; } struct aws_byte_cursor cert_cursor = aws_byte_cursor_from_string(tls->certificate); struct aws_byte_cursor key_cursor = aws_byte_cursor_from_string(tls->private_key); if (aws_tls_ctx_options_init_client_mtls(&tls->options, allocator, &cert_cursor, &key_cursor)) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls failed"); goto on_error; } } else if (jni_cert_path && jni_key_path) { tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_cert_path); if (!tls->certificate_path) { aws_jni_throw_runtime_exception(env, "failed to get certificatePath string"); goto on_error; } tls->private_key_path = aws_jni_new_string_from_jstring(env, jni_key_path); if (!tls->private_key_path) { aws_jni_throw_runtime_exception(env, "failed to get privateKeyPath string"); goto on_error; } if (aws_tls_ctx_options_init_client_mtls_from_path( &tls->options, allocator, aws_string_c_str(tls->certificate_path), aws_string_c_str(tls->private_key_path))) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_path failed"); goto on_error; } } else if (jni_pkcs11_options) { tls->pkcs11_options = aws_tls_ctx_pkcs11_options_from_java_new(env, jni_pkcs11_options); if (tls->pkcs11_options == NULL) { /* exception already thrown */ goto on_error; } if (aws_tls_ctx_options_init_client_mtls_with_pkcs11(&tls->options, allocator, tls->pkcs11_options)) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_with_pkcs11 failed"); goto on_error; } } else if (jni_custom_key_op) { jobject jni_custom_key_op_handle = (*env)->GetObjectField( env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.operation_handler_field_id); if (!jni_custom_key_op_handle) { aws_jni_throw_runtime_exception( env, "could not get custom operation handler from jni_custom_key_op_handle!"); goto on_error; } tls->custom_key_op_handler = aws_custom_key_op_handler_java_new(env, jni_custom_key_op_handle); /* Certificate needs to be set, but there are multiple ways to get it */ jstring jni_custom_key_op_cert_path = (*env)->GetObjectField( env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.certificate_file_path_field_id); jstring jni_custom_key_op_cert_contents = (*env)->GetObjectField( env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.certificate_file_contents_field_id); if (jni_custom_key_op_cert_path && jni_custom_key_op_cert_contents) { /* Cannot have both a certificate path and data - use one or the other */ aws_jni_throw_runtime_exception( env, "Custom key operation handler: cannot have both certificate file path and certificate contents!"); goto on_error; } else if (jni_custom_key_op_cert_contents) { /* If we have the certificate contents, then use it directly */ tls->certificate = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_contents); if (!tls->certificate) { aws_jni_throw_runtime_exception( env, "Custom key operation handler: failed to get certificate contents string"); goto on_error; } struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_string(tls->certificate); /* Initialize the client with a custom key operation */ if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations( &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) { aws_jni_throw_runtime_exception( env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed"); goto on_error; } } else if (jni_custom_key_op_cert_path) { /* If we have a certificate path, we need to get the certificate data from it and use that */ tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_path); if (!tls->certificate_path) { aws_jni_throw_runtime_exception( env, "Custom key operation handler: failed to get certificate path string"); goto on_error; } struct aws_byte_buf tmp_byte_buf; int op = aws_byte_buf_init_from_file(&tmp_byte_buf, allocator, aws_string_c_str(tls->certificate_path)); if (op != AWS_OP_SUCCESS) { aws_jni_throw_runtime_exception( env, "Custom key operation handler: failed to get certificate path string"); aws_byte_buf_clean_up(&tmp_byte_buf); goto on_error; } struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_buf(&tmp_byte_buf); /* Initialize the client with a custom key operation */ if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations( &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) { aws_jni_throw_runtime_exception( env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed"); aws_byte_buf_clean_up(&tmp_byte_buf); goto on_error; } aws_byte_buf_clean_up(&tmp_byte_buf); } else { aws_jni_throw_runtime_exception(env, "Custom key operation handler: No certificate set!"); goto on_error; } } else if (jni_pkcs12_path && jni_pkcs12_password) { tls->pkcs12_path = aws_jni_new_string_from_jstring(env, jni_pkcs12_path); if (!tls->pkcs12_path) { aws_jni_throw_runtime_exception(env, "failed to get pkcs12Path string"); goto on_error; } tls->pkcs12_password = aws_jni_new_string_from_jstring(env, jni_pkcs12_password); if (!tls->pkcs12_password) { aws_jni_throw_runtime_exception(env, "failed to get pkcs12Password string"); goto on_error; } struct aws_byte_cursor password = aws_byte_cursor_from_string(tls->pkcs12_password); if (aws_tls_ctx_options_init_client_mtls_pkcs12_from_path( &tls->options, allocator, aws_string_c_str(tls->pkcs12_path), &password)) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_pkcs12_from_path failed"); goto on_error; } } else if (jni_windows_cert_store_path) { tls->windows_cert_store_path = aws_jni_new_string_from_jstring(env, jni_windows_cert_store_path); if (!tls->windows_cert_store_path) { aws_jni_throw_runtime_exception(env, "failed to get windowsCertStorePath string"); goto on_error; } if (aws_tls_ctx_options_init_client_mtls_from_system_path( &tls->options, allocator, aws_string_c_str(tls->windows_cert_store_path))) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_system_path failed"); goto on_error; } } if (jni_ca) { tls->ca_root = aws_jni_new_string_from_jstring(env, jni_ca); if (!tls->ca_root) { aws_jni_throw_runtime_exception(env, "failed to get caRoot string"); goto on_error; } struct aws_byte_cursor ca_cursor = aws_byte_cursor_from_string(tls->ca_root); if (aws_tls_ctx_options_override_default_trust_store(&tls->options, &ca_cursor)) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store failed"); goto on_error; } } else if (jni_ca_filepath || jni_ca_dirpath) { const char *ca_file = NULL; const char *ca_path = NULL; if (jni_ca_filepath) { tls->ca_file = aws_jni_new_string_from_jstring(env, jni_ca_filepath); if (!tls->ca_file) { aws_jni_throw_runtime_exception(env, "failed to get caFile string"); goto on_error; } ca_file = aws_string_c_str(tls->ca_file); } if (jni_ca_dirpath) { tls->ca_path = aws_jni_new_string_from_jstring(env, jni_ca_dirpath); if (!tls->ca_path) { aws_jni_throw_runtime_exception(env, "failed to get caPath string"); goto on_error; } ca_path = aws_string_c_str(tls->ca_path); } if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls->options, ca_path, ca_file)) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store_from_path failed"); goto on_error; } } /* apply the rest of the non-init settings */ tls->options.minimum_tls_version = (enum aws_tls_versions)jni_min_tls_version; tls->options.cipher_pref = (enum aws_tls_cipher_pref)jni_cipher_pref; tls->options.verify_peer = jni_verify_peer != 0; if (jni_alpn) { tls->alpn_list = aws_jni_new_string_from_jstring(env, jni_alpn); if (!tls->alpn_list) { aws_jni_throw_runtime_exception(env, "failed to get alpnList string"); goto on_error; } if (aws_tls_ctx_options_set_alpn_list(&tls->options, aws_string_c_str(tls->alpn_list))) { aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_set_alpn_list failed"); goto on_error; } } return (jlong)tls; on_error: s_jni_tls_ctx_options_destroy(tls); return (jlong)0; } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsDestroy( JNIEnv *env, jclass jni_class, jlong jni_tls) { (void)jni_class; aws_cache_jni_ids(env); s_jni_tls_ctx_options_destroy((struct jni_tls_ctx_options *)jni_tls); } JNIEXPORT jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsAlpnAvailable( JNIEnv *env, jclass jni_class) { (void)jni_class; aws_cache_jni_ids(env); return aws_tls_is_alpn_available(); } JNIEXPORT jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsCipherPreferenceSupported( JNIEnv *env, jclass jni_class, jint jni_cipher_pref) { (void)jni_class; aws_cache_jni_ids(env); if (jni_cipher_pref < 0 || AWS_IO_TLS_CIPHER_PREF_END_RANGE <= jni_cipher_pref) { aws_jni_throw_runtime_exception( env, "TlsContextOptions.tlsContextOptionsSetCipherPreference: TlsCipherPreference is out of range: %d", (int)jni_cipher_pref); return false; } return aws_tls_is_cipher_pref_supported((enum aws_tls_cipher_pref)jni_cipher_pref); } #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(pop) # else # pragma GCC diagnostic pop # endif #endif