1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include <jni.h>
7
8 #include <aws/common/string.h>
9 #include <aws/io/tls_channel_handler.h>
10
11 #include "crt.h"
12 #include "java_class_ids.h"
13 #include "tls_context_pkcs11_options.h"
14
15 #include "custom_key_op_handler.h"
16
17 /* Have to wrap the native struct so we can manage string lifetime */
18 struct jni_tls_ctx_options {
19 /* Must be first thing in the structure so casts to aws_tls_ctx_options work */
20 struct aws_tls_ctx_options options;
21 /* these strings get copied from java, so we don't have to pin and track references */
22 struct aws_string *ca_file;
23 struct aws_string *ca_path;
24 struct aws_string *alpn_list;
25 struct aws_string *certificate_path;
26 struct aws_string *private_key_path;
27 struct aws_string *pkcs12_path;
28 struct aws_string *pkcs12_password;
29 struct aws_string *certificate;
30 struct aws_string *private_key;
31 struct aws_string *windows_cert_store_path;
32 struct aws_string *ca_root;
33
34 struct aws_tls_ctx_pkcs11_options *pkcs11_options;
35
36 struct aws_custom_key_op_handler *custom_key_op_handler;
37 };
38
39 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
40 #if UINTPTR_MAX == 0xffffffff
41 # if defined(_MSC_VER)
42 # pragma warning(push)
43 # pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
44 # else
45 # pragma GCC diagnostic push
46 # pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
47 # pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
48 # endif
49 #endif
50
s_jni_tls_ctx_options_destroy(struct jni_tls_ctx_options * tls)51 static void s_jni_tls_ctx_options_destroy(struct jni_tls_ctx_options *tls) {
52 if (tls == NULL) {
53 return;
54 }
55
56 aws_string_destroy(tls->ca_file);
57 aws_string_destroy(tls->ca_path);
58 aws_string_destroy(tls->alpn_list);
59 aws_string_destroy(tls->certificate_path);
60 aws_string_destroy(tls->private_key_path);
61 aws_string_destroy(tls->pkcs12_path);
62 aws_string_destroy_secure(tls->pkcs12_password);
63 aws_string_destroy(tls->certificate);
64 aws_string_destroy_secure(tls->private_key);
65 aws_string_destroy(tls->windows_cert_store_path);
66 aws_string_destroy(tls->ca_root);
67
68 aws_tls_ctx_pkcs11_options_from_java_destroy(tls->pkcs11_options);
69 aws_custom_key_op_handler_java_release(tls->custom_key_op_handler);
70 aws_tls_ctx_options_clean_up(&tls->options);
71
72 struct aws_allocator *allocator = aws_jni_get_allocator();
73 aws_mem_release(allocator, tls);
74 }
75
76 JNIEXPORT
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)77 jlong JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsNew(
78 JNIEnv *env,
79 jclass jni_class,
80 jint jni_min_tls_version,
81 jint jni_cipher_pref,
82 jstring jni_alpn,
83 jstring jni_certificate,
84 jstring jni_private_key,
85 jstring jni_cert_path,
86 jstring jni_key_path,
87 jstring jni_ca,
88 jstring jni_ca_filepath,
89 jstring jni_ca_dirpath,
90 jboolean jni_verify_peer,
91 jstring jni_pkcs12_path,
92 jstring jni_pkcs12_password,
93 jobject jni_pkcs11_options,
94 jobject jni_custom_key_op,
95 jstring jni_windows_cert_store_path) {
96 (void)jni_class;
97 aws_cache_jni_ids(env);
98
99 struct aws_allocator *allocator = aws_jni_get_allocator();
100 struct jni_tls_ctx_options *tls = aws_mem_calloc(allocator, 1, sizeof(struct jni_tls_ctx_options));
101 AWS_FATAL_ASSERT(tls);
102 aws_tls_ctx_options_init_default_client(&tls->options, allocator);
103
104 /* Certs or paths will cause an init, which overwrites other fields, so do those first */
105 if (jni_certificate && jni_private_key) {
106 tls->certificate = aws_jni_new_string_from_jstring(env, jni_certificate);
107 if (!tls->certificate) {
108 aws_jni_throw_runtime_exception(env, "failed to get certificate string");
109 goto on_error;
110 }
111 tls->private_key = aws_jni_new_string_from_jstring(env, jni_private_key);
112 if (!tls->private_key) {
113 aws_jni_throw_runtime_exception(env, "failed to get privateKey string");
114 goto on_error;
115 }
116
117 struct aws_byte_cursor cert_cursor = aws_byte_cursor_from_string(tls->certificate);
118 struct aws_byte_cursor key_cursor = aws_byte_cursor_from_string(tls->private_key);
119
120 if (aws_tls_ctx_options_init_client_mtls(&tls->options, allocator, &cert_cursor, &key_cursor)) {
121 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls failed");
122 goto on_error;
123 }
124 } else if (jni_cert_path && jni_key_path) {
125 tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_cert_path);
126 if (!tls->certificate_path) {
127 aws_jni_throw_runtime_exception(env, "failed to get certificatePath string");
128 goto on_error;
129 }
130 tls->private_key_path = aws_jni_new_string_from_jstring(env, jni_key_path);
131 if (!tls->private_key_path) {
132 aws_jni_throw_runtime_exception(env, "failed to get privateKeyPath string");
133 goto on_error;
134 }
135
136 if (aws_tls_ctx_options_init_client_mtls_from_path(
137 &tls->options,
138 allocator,
139 aws_string_c_str(tls->certificate_path),
140 aws_string_c_str(tls->private_key_path))) {
141 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_path failed");
142 goto on_error;
143 }
144 } else if (jni_pkcs11_options) {
145 tls->pkcs11_options = aws_tls_ctx_pkcs11_options_from_java_new(env, jni_pkcs11_options);
146 if (tls->pkcs11_options == NULL) {
147 /* exception already thrown */
148 goto on_error;
149 }
150
151 if (aws_tls_ctx_options_init_client_mtls_with_pkcs11(&tls->options, allocator, tls->pkcs11_options)) {
152 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_with_pkcs11 failed");
153 goto on_error;
154 }
155 } else if (jni_custom_key_op) {
156
157 jobject jni_custom_key_op_handle = (*env)->GetObjectField(
158 env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.operation_handler_field_id);
159 if (!jni_custom_key_op_handle) {
160 aws_jni_throw_runtime_exception(
161 env, "could not get custom operation handler from jni_custom_key_op_handle!");
162 goto on_error;
163 }
164
165 tls->custom_key_op_handler = aws_custom_key_op_handler_java_new(env, jni_custom_key_op_handle);
166
167 /* Certificate needs to be set, but there are multiple ways to get it */
168 jstring jni_custom_key_op_cert_path = (*env)->GetObjectField(
169 env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.certificate_file_path_field_id);
170 jstring jni_custom_key_op_cert_contents = (*env)->GetObjectField(
171 env,
172 jni_custom_key_op,
173 tls_context_custom_key_operation_options_properties.certificate_file_contents_field_id);
174
175 if (jni_custom_key_op_cert_path && jni_custom_key_op_cert_contents) {
176 /* Cannot have both a certificate path and data - use one or the other */
177 aws_jni_throw_runtime_exception(
178 env, "Custom key operation handler: cannot have both certificate file path and certificate contents!");
179 goto on_error;
180 } else if (jni_custom_key_op_cert_contents) {
181 /* If we have the certificate contents, then use it directly */
182 tls->certificate = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_contents);
183 if (!tls->certificate) {
184 aws_jni_throw_runtime_exception(
185 env, "Custom key operation handler: failed to get certificate contents string");
186 goto on_error;
187 }
188 struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_string(tls->certificate);
189
190 /* Initialize the client with a custom key operation */
191 if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations(
192 &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) {
193 aws_jni_throw_runtime_exception(
194 env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed");
195 goto on_error;
196 }
197 } else if (jni_custom_key_op_cert_path) {
198 /* If we have a certificate path, we need to get the certificate data from it and use that */
199 tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_path);
200 if (!tls->certificate_path) {
201 aws_jni_throw_runtime_exception(
202 env, "Custom key operation handler: failed to get certificate path string");
203 goto on_error;
204 }
205 struct aws_byte_buf tmp_byte_buf;
206 int op = aws_byte_buf_init_from_file(&tmp_byte_buf, allocator, aws_string_c_str(tls->certificate_path));
207 if (op != AWS_OP_SUCCESS) {
208 aws_jni_throw_runtime_exception(
209 env, "Custom key operation handler: failed to get certificate path string");
210 aws_byte_buf_clean_up(&tmp_byte_buf);
211 goto on_error;
212 }
213 struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_buf(&tmp_byte_buf);
214
215 /* Initialize the client with a custom key operation */
216 if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations(
217 &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) {
218 aws_jni_throw_runtime_exception(
219 env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed");
220 aws_byte_buf_clean_up(&tmp_byte_buf);
221 goto on_error;
222 }
223 aws_byte_buf_clean_up(&tmp_byte_buf);
224 } else {
225 aws_jni_throw_runtime_exception(env, "Custom key operation handler: No certificate set!");
226 goto on_error;
227 }
228
229 } else if (jni_pkcs12_path && jni_pkcs12_password) {
230 tls->pkcs12_path = aws_jni_new_string_from_jstring(env, jni_pkcs12_path);
231 if (!tls->pkcs12_path) {
232 aws_jni_throw_runtime_exception(env, "failed to get pkcs12Path string");
233 goto on_error;
234 }
235 tls->pkcs12_password = aws_jni_new_string_from_jstring(env, jni_pkcs12_password);
236 if (!tls->pkcs12_password) {
237 aws_jni_throw_runtime_exception(env, "failed to get pkcs12Password string");
238 goto on_error;
239 }
240
241 struct aws_byte_cursor password = aws_byte_cursor_from_string(tls->pkcs12_password);
242 if (aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(
243 &tls->options, allocator, aws_string_c_str(tls->pkcs12_path), &password)) {
244 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_pkcs12_from_path failed");
245 goto on_error;
246 }
247 } else if (jni_windows_cert_store_path) {
248 tls->windows_cert_store_path = aws_jni_new_string_from_jstring(env, jni_windows_cert_store_path);
249 if (!tls->windows_cert_store_path) {
250 aws_jni_throw_runtime_exception(env, "failed to get windowsCertStorePath string");
251 goto on_error;
252 }
253
254 if (aws_tls_ctx_options_init_client_mtls_from_system_path(
255 &tls->options, allocator, aws_string_c_str(tls->windows_cert_store_path))) {
256
257 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_system_path failed");
258 goto on_error;
259 }
260 }
261
262 if (jni_ca) {
263 tls->ca_root = aws_jni_new_string_from_jstring(env, jni_ca);
264 if (!tls->ca_root) {
265 aws_jni_throw_runtime_exception(env, "failed to get caRoot string");
266 goto on_error;
267 }
268 struct aws_byte_cursor ca_cursor = aws_byte_cursor_from_string(tls->ca_root);
269 if (aws_tls_ctx_options_override_default_trust_store(&tls->options, &ca_cursor)) {
270 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store failed");
271 goto on_error;
272 }
273 } else if (jni_ca_filepath || jni_ca_dirpath) {
274 const char *ca_file = NULL;
275 const char *ca_path = NULL;
276 if (jni_ca_filepath) {
277 tls->ca_file = aws_jni_new_string_from_jstring(env, jni_ca_filepath);
278 if (!tls->ca_file) {
279 aws_jni_throw_runtime_exception(env, "failed to get caFile string");
280 goto on_error;
281 }
282
283 ca_file = aws_string_c_str(tls->ca_file);
284 }
285 if (jni_ca_dirpath) {
286 tls->ca_path = aws_jni_new_string_from_jstring(env, jni_ca_dirpath);
287 if (!tls->ca_path) {
288 aws_jni_throw_runtime_exception(env, "failed to get caPath string");
289 goto on_error;
290 }
291
292 ca_path = aws_string_c_str(tls->ca_path);
293 }
294
295 if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls->options, ca_path, ca_file)) {
296 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store_from_path failed");
297 goto on_error;
298 }
299 }
300
301 /* apply the rest of the non-init settings */
302 tls->options.minimum_tls_version = (enum aws_tls_versions)jni_min_tls_version;
303 tls->options.cipher_pref = (enum aws_tls_cipher_pref)jni_cipher_pref;
304 tls->options.verify_peer = jni_verify_peer != 0;
305
306 if (jni_alpn) {
307 tls->alpn_list = aws_jni_new_string_from_jstring(env, jni_alpn);
308 if (!tls->alpn_list) {
309 aws_jni_throw_runtime_exception(env, "failed to get alpnList string");
310 goto on_error;
311 }
312
313 if (aws_tls_ctx_options_set_alpn_list(&tls->options, aws_string_c_str(tls->alpn_list))) {
314 aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_set_alpn_list failed");
315 goto on_error;
316 }
317 }
318
319 return (jlong)tls;
320
321 on_error:
322
323 s_jni_tls_ctx_options_destroy(tls);
324
325 return (jlong)0;
326 }
327
328 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsDestroy(JNIEnv * env,jclass jni_class,jlong jni_tls)329 void JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsDestroy(
330 JNIEnv *env,
331 jclass jni_class,
332 jlong jni_tls) {
333 (void)jni_class;
334 aws_cache_jni_ids(env);
335
336 s_jni_tls_ctx_options_destroy((struct jni_tls_ctx_options *)jni_tls);
337 }
338
339 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsAlpnAvailable(JNIEnv * env,jclass jni_class)340 jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsAlpnAvailable(
341 JNIEnv *env,
342 jclass jni_class) {
343 (void)jni_class;
344 aws_cache_jni_ids(env);
345
346 return aws_tls_is_alpn_available();
347 }
348
349 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsCipherPreferenceSupported(JNIEnv * env,jclass jni_class,jint jni_cipher_pref)350 jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsCipherPreferenceSupported(
351 JNIEnv *env,
352 jclass jni_class,
353 jint jni_cipher_pref) {
354
355 (void)jni_class;
356 aws_cache_jni_ids(env);
357
358 if (jni_cipher_pref < 0 || AWS_IO_TLS_CIPHER_PREF_END_RANGE <= jni_cipher_pref) {
359 aws_jni_throw_runtime_exception(
360 env,
361 "TlsContextOptions.tlsContextOptionsSetCipherPreference: TlsCipherPreference is out of range: %d",
362 (int)jni_cipher_pref);
363 return false;
364 }
365
366 return aws_tls_is_cipher_pref_supported((enum aws_tls_cipher_pref)jni_cipher_pref);
367 }
368
369 #if UINTPTR_MAX == 0xffffffff
370 # if defined(_MSC_VER)
371 # pragma warning(pop)
372 # else
373 # pragma GCC diagnostic pop
374 # endif
375 #endif
376