diff --git a/native/yaha_native/src/binding.rs b/native/yaha_native/src/binding.rs index c03791d..b803c81 100644 --- a/native/yaha_native/src/binding.rs +++ b/native/yaha_native/src/binding.rs @@ -70,6 +70,34 @@ pub extern "C" fn yaha_client_config_add_root_certificates(ctx: *mut YahaNativeC valid } +#[no_mangle] +pub extern "C" fn yaha_client_config_add_client_auth_certificates(ctx: *mut YahaNativeContext, auth_certs: *const StringBuffer) -> usize { + let ctx = YahaNativeContextInternal::from_raw_context(ctx); + let certs: Vec = unsafe { rustls_pemfile::certs(&mut (*auth_certs).to_bytes()).unwrap().into_iter().map(rustls::Certificate).collect() }; + + let count = certs.len(); + + if count > 0 { + ctx.client_auth_certificates.get_or_insert(certs); + } + + count +} + +#[no_mangle] +pub extern "C" fn yaha_client_config_add_client_auth_key(ctx: *mut YahaNativeContext, auth_key: *const StringBuffer) -> usize { + let ctx = YahaNativeContextInternal::from_raw_context(ctx); + let keys: Vec = unsafe { rustls_pemfile::pkcs8_private_keys(&mut (*auth_key).to_bytes()).unwrap().into_iter().map(rustls::PrivateKey).collect() }; + + let count = keys.len(); + + if count > 0 { + ctx.client_auth_key.get_or_insert(keys[0].clone()); + } + + count +} + #[no_mangle] pub extern "C" fn yaha_client_config_skip_certificate_verification(ctx: *mut YahaNativeContext, val: bool) { let ctx = YahaNativeContextInternal::from_raw_context(ctx); diff --git a/native/yaha_native/src/context.rs b/native/yaha_native/src/context.rs index f9bdd05..ad41b0a 100644 --- a/native/yaha_native/src/context.rs +++ b/native/yaha_native/src/context.rs @@ -20,6 +20,8 @@ pub struct YahaNativeContextInternal { pub client_builder: Option, pub skip_certificate_verification: Option, pub root_certificates: Option, + pub client_auth_certificates: Option>, + pub client_auth_key: Option, pub client: Option, hyper::Body>>, pub on_status_code_and_headers_receive: OnStatusCodeAndHeadersReceive, pub on_receive: OnReceive, @@ -42,6 +44,8 @@ impl YahaNativeContextInternal { client_builder: Some(Client::builder()), skip_certificate_verification: None, root_certificates: None, + client_auth_certificates: None, + client_auth_key: None, on_status_code_and_headers_receive, on_receive, on_complete, @@ -66,14 +70,23 @@ impl YahaNativeContextInternal { .with_custom_certificate_verifier(Arc::new(danger::NoCertificateVerification {})) .with_no_client_auth(); } else { + let tls_config_builder_root: rustls::ConfigBuilder; if let Some(root_certificates) = &self.root_certificates { - tls_config = tls_config_builder - .with_root_certificates(root_certificates.to_owned()) - .with_no_client_auth(); + tls_config_builder_root = tls_config_builder.with_root_certificates(root_certificates.to_owned()); } else { - tls_config = tls_config_builder - .with_webpki_roots() - .with_no_client_auth(); + tls_config_builder_root = tls_config_builder.with_webpki_roots(); + } + + tls_config = if let Some(client_auth_certificates) = &self.client_auth_certificates { + if let Some(client_auth_key) = &self.client_auth_key { + tls_config_builder_root.clone() + .with_client_auth_cert(client_auth_certificates.to_owned(), client_auth_key.to_owned()) + .unwrap_or(tls_config_builder_root.with_no_client_auth()) + } else { + tls_config_builder_root.with_no_client_auth() + } + } else { + tls_config_builder_root.with_no_client_auth() } } diff --git a/src/YetAnotherHttpHandler/NativeMethods.g.cs b/src/YetAnotherHttpHandler/NativeMethods.g.cs index d38b53f..2db6c3a 100644 --- a/src/YetAnotherHttpHandler/NativeMethods.g.cs +++ b/src/YetAnotherHttpHandler/NativeMethods.g.cs @@ -42,6 +42,12 @@ internal static unsafe partial class NativeMethods [DllImport(__DllName, EntryPoint = "yaha_client_config_add_root_certificates", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern nuint yaha_client_config_add_root_certificates(YahaNativeContext* ctx, StringBuffer* root_certs); + [DllImport(__DllName, EntryPoint = "yaha_client_config_add_client_auth_certificates", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint yaha_client_config_add_client_auth_certificates(YahaNativeContext* ctx, StringBuffer* auth_certs); + + [DllImport(__DllName, EntryPoint = "yaha_client_config_add_client_auth_key", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint yaha_client_config_add_client_auth_key(YahaNativeContext* ctx, StringBuffer* auth_key); + [DllImport(__DllName, EntryPoint = "yaha_client_config_skip_certificate_verification", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_skip_certificate_verification(YahaNativeContext* ctx, [MarshalAs(UnmanagedType.U1)] bool val); diff --git a/src/YetAnotherHttpHandler/NativeMethodsFuncPtr.g.cs b/src/YetAnotherHttpHandler/NativeMethodsFuncPtr.g.cs index 8665a29..cc5a64e 100644 --- a/src/YetAnotherHttpHandler/NativeMethodsFuncPtr.g.cs +++ b/src/YetAnotherHttpHandler/NativeMethodsFuncPtr.g.cs @@ -28,6 +28,12 @@ internal static unsafe partial class NativeMethodsFuncPtr [DllImport(__DllName, EntryPoint = "yaha_client_config_add_root_certificates", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern nuint yaha_client_config_add_root_certificates(YahaNativeContext* ctx, StringBuffer* root_certs); + [DllImport(__DllName, EntryPoint = "yaha_client_config_add_client_auth_certificates", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint yaha_client_config_add_client_auth_certificates(YahaNativeContext* ctx, StringBuffer* auth_certs); + + [DllImport(__DllName, EntryPoint = "yaha_client_config_add_client_auth_key", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint yaha_client_config_add_client_auth_key(YahaNativeContext* ctx, StringBuffer* auth_key); + [DllImport(__DllName, EntryPoint = "yaha_client_config_skip_certificate_verification", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_skip_certificate_verification(YahaNativeContext* ctx, [MarshalAs(UnmanagedType.U1)] bool val); diff --git a/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs b/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs index c365f56..f0c2788 100644 --- a/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs +++ b/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs @@ -50,6 +50,16 @@ public class YetAnotherHttpHandler : HttpMessageHandler /// public string? RootCertificates { get => _settings.RootCertificates; set => _settings.RootCertificates = value; } + /// + /// Gets or sets a custom client auth certificates. + /// + public string? ClientAuthCertificates { get => _settings.ClientAuthCertificates; set => _settings.ClientAuthCertificates = value; } + + /// + /// Gets or sets a custom client auth key. + /// + public string? ClientAuthKey { get => _settings.ClientAuthKey; set => _settings.ClientAuthKey = value; } + /// /// Gets or sets the SETTINGS_INITIAL_WINDOW_SIZE option for HTTP2 stream-level flow control. /// @@ -174,6 +184,8 @@ internal class NativeClientSettings public bool? Http2Only { get; set; } public bool? SkipCertificateVerification { get; set; } public string? RootCertificates { get; set; } + public string? ClientAuthCertificates { get; set; } + public string? ClientAuthKey { get; set; } public uint? Http2InitialStreamWindowSize { get; set; } public uint? Http2InitialConnectionWindowSize { get; set; } public bool? Http2AdaptiveWindow { get; set; } @@ -193,6 +205,8 @@ public NativeClientSettings Clone() Http2Only = this.Http2Only, SkipCertificateVerification = this.SkipCertificateVerification, RootCertificates = this.RootCertificates, + ClientAuthCertificates = this.ClientAuthCertificates, + ClientAuthKey = this.ClientAuthKey, Http2InitialStreamWindowSize = this.Http2InitialStreamWindowSize, Http2InitialConnectionWindowSize = this.Http2InitialConnectionWindowSize, Http2AdaptiveWindow = this.Http2AdaptiveWindow,