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

Exception of type 'Interop+AndroidCrypto+SslException' was thrown. #74292

Closed
ComptonAlvaro opened this issue Aug 20, 2022 · 21 comments
Closed

Exception of type 'Interop+AndroidCrypto+SslException' was thrown. #74292

ComptonAlvaro opened this issue Aug 20, 2022 · 21 comments

Comments

@ComptonAlvaro
Copy link

ComptonAlvaro commented Aug 20, 2022

I want to connect to a gRPC service that is hosted in ASP Core application. All my projects are .NET 6.

In the client side, I have a a library that is the gRPC client, that receive the certificates of the client in a string format and then it creates the channel and the connection to the service. In this way, I can use this library in many clients, WPF, MAUI and so on.

When I use the WPF client, it works, I can connect with the remote server, but when I use the MAUI project, I get an error. Is this error:

Exception of type 'Interop+AndroidCrypto+SslException' was thrown.

{Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__175`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.Http2Connection, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.HttpConnectionPool.GetHttp2ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.<RunCall>d__73[[GestorOrdenadores.Service.Client.Grpc.HelloRequest, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[GestorOrdenadores.Service.Client.Grpc.HelloReply, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in /_/src/Grpc.Net.Client/Internal/GrpcCall.cs:line 493")
   at GestorOrdenadores.Service.Client.Grpc.ServiceClientGrpc.SayHelloAsync(String paramNombre) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Service.Client.Grpc\ServiceClientGrpc.cs:line 71
   at GestorOrdenadores.Client.Maui.MainPage.OnBtnPingClicked(Object sender, EventArgs e) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Client.Maui\MainPage.xaml.cs:line 49}

The code of the library is this:

public class ServiceClientGrpc
    {
        #region constructores
        public ServiceClientGrpc(string paramDireccion, int paramPuerto, string paramCertificado, string paramKey)
        {
            var cert = X509Certificate2.CreateFromPem(paramCertificado, paramKey);

            paramDireccion = @"https://192.168.1.200";
            paramPuerto = 5001;


            HttpClientHandler miHttpHandler = new HttpClientHandler();
            miHttpHandler.ClientCertificates.Add(cert);
            HttpClient httpClient = new(miHttpHandler);

            miHttpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

            var channel = GrpcChannel.ForAddress(paramDireccion + ":" + paramPuerto, new GrpcChannelOptions
            {
                HttpClient = httpClient
            });





            _client = new Greeter.GreeterClient(channel);
        }
        #endregion constructores


        private readonly Greeter.GreeterClient _client;



        public async Task<string> SayHelloAsync(string paramNombre)
        {
            HelloRequest miRequest = new HelloRequest()
            {
                Name = paramNombre
            };

            return (await _client.SayHelloAsync(miRequest)).Message;
        }
    }

The WPF client:

private async void BtnSaludar_Click(object sender, RoutedEventArgs e)
{
    try
    {
        string miStrCertificado = System.IO.File.ReadAllText(@"Certificados\Client.crt");
        string miStrClave = System.IO.File.ReadAllText(@"Certificados\Client.key");

        ServiceClientGrpc miService = new ServiceClientGrpc("https://192.168.1.200", 5001, miStrCertificado, miStrClave);


        MessageBox.Show(await miService.SayHelloAsync("Álvaro"));
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The MAUI client:

private async void OnBtnSaludarClicked(object sender, EventArgs e)
{
    try
    {
        //Get address and port from XML file.
        string miStrDireccion = Configuracion.GestorConfiguracion.GetDireccionServicio();
        int miStrPuerto = Configuracion.GestorConfiguracion.GetPuertoServicio();

        //Get the certificates from Asserts files
        using Stream miStmCertificado = await FileSystem.OpenAppPackageFileAsync("certificados/client.crt");
        using StreamReader miSrCertificado = new StreamReader(miStmCertificado);
        string miStrCertificado = await miSrCertificado.ReadToEndAsync();

        using Stream miStmKey = await FileSystem.OpenAppPackageFileAsync("certificados/client.key");
        using StreamReader miSrKey = new StreamReader(miStmKey);
        string miStrKey = await miSrKey.ReadToEndAsync();


        ServiceClientGrpc miServicio = new ServiceClientGrpc(miStrDireccion, miStrPuerto, miStrCertificado, miStrKey);

        EtrRespuestaPing.Text = await miServicio.SayHelloAsync("Álvaro"); //Excetion in this line
    }
    catch(Exception ex)
    {
        await DisplayAlert("Alert", ex.Message, "OK");
    }
}

From the android device in which I am debugging the MAUI project, I can ping the server with an application that pings.

Really the code is the same in WPF and MAUI, just I instantiate an object of type ServiceClientGrpc, passing the same data. Is ServiceClientGrpc who creates the channel and call the service.

In the past, when I tried this with Xamarin, I read that there was limitations in Xamarin, but I though that now MAUI can use Net 6, I had hope that I could use the same library for all the clients.

Is it not possible to use the same library for windows clients and android clients?

Thanks.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Aug 20, 2022
@ghost
Copy link

ghost commented Aug 20, 2022

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

I want to connect to a gRPC service that is hosted in ASP Core application. All my projects are .NET 6.

In the client side, I have a a library that is the gRPC client, that receive the certificates of the client in a string format and then it creates the channel and the connection to the service. In this way, I can use this library in many clients, WPF, MAUI and so on.

When I use the WPF client, it works, I can connect with the remote server, but when I use the MAUI project, I get an error. Is this error:


Exception of type 'Interop+AndroidCrypto+SslException' was thrown.

{Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__175`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.Http2Connection, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.HttpConnectionPool.GetHttp2ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.<RunCall>d__73[[GestorOrdenadores.Service.Client.Grpc.HelloRequest, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[GestorOrdenadores.Service.Client.Grpc.HelloReply, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in /_/src/Grpc.Net.Client/Internal/GrpcCall.cs:line 493")
   at GestorOrdenadores.Service.Client.Grpc.ServiceClientGrpc.SayHelloAsync(String paramNombre) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Service.Client.Grpc\ServiceClientGrpc.cs:line 71
   at GestorOrdenadores.Client.Maui.MainPage.OnBtnPingClicked(Object sender, EventArgs e) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Client.Maui\MainPage.xaml.cs:line 49}

The code of the library is this:

public class ServiceClientGrpc
    {
        #region constructores
        public ServiceClientGrpc(string paramDireccion, int paramPuerto, string paramCertificado, string paramKey)
        {
            var cert = X509Certificate2.CreateFromPem(paramCertificado, paramKey);

            paramDireccion = @"https://192.168.1.200";
            paramPuerto = 5001;


            HttpClientHandler miHttpHandler = new HttpClientHandler();
            miHttpHandler.ClientCertificates.Add(cert);
            HttpClient httpClient = new(miHttpHandler);

            miHttpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

            var channel = GrpcChannel.ForAddress(paramDireccion + ":" + paramPuerto, new GrpcChannelOptions
            {
                HttpClient = httpClient
            });





            _client = new Greeter.GreeterClient(channel);
        }
        #endregion constructores


        private readonly Greeter.GreeterClient _client;



        public async Task<string> SayHelloAsync(string paramNombre)
        {
            HelloRequest miRequest = new HelloRequest()
            {
                Name = paramNombre
            };

            return (await _client.SayHelloAsync(miRequest)).Message;
        }
    }

The WPF client:

private async void BtnSaludar_Click(object sender, RoutedEventArgs e)
{
    try
    {
        string miStrCertificado = System.IO.File.ReadAllText(@"Certificados\Client.crt");
        string miStrClave = System.IO.File.ReadAllText(@"Certificados\Client.key");

        ServiceClientGrpc miService = new ServiceClientGrpc("https://192.168.1.200", 5001, miStrCertificado, miStrClave);


        MessageBox.Show(await miService.SayHelloAsync("Álvaro"));
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The MAUI client:

private async void OnBtnSaludarClicked(object sender, EventArgs e)
{
    try
    {
        //Get address and port from XML file.
        string miStrDireccion = Configuracion.GestorConfiguracion.GetDireccionServicio();
        int miStrPuerto = Configuracion.GestorConfiguracion.GetPuertoServicio();

        //Get the certificates from Asserts files
        using Stream miStmCertificado = await FileSystem.OpenAppPackageFileAsync("certificados/client.crt");
        using StreamReader miSrCertificado = new StreamReader(miStmCertificado);
        string miStrCertificado = await miSrCertificado.ReadToEndAsync();

        using Stream miStmKey = await FileSystem.OpenAppPackageFileAsync("certificados/client.key");
        using StreamReader miSrKey = new StreamReader(miStmKey);
        string miStrKey = await miSrKey.ReadToEndAsync();


        ServiceClientGrpc miServicio = new ServiceClientGrpc(miStrDireccion, miStrPuerto, miStrCertificado, miStrKey);

        EtrRespuestaPing.Text = await miServicio.SayHelloAsync("Álvaro"); //Excetion in this line
    }
    catch(Exception ex)
    {
        await DisplayAlert("Alert", ex.Message, "OK");
    }
}

From the android device in which I am debugging the MAUI project, I can ping the server with an application that pings.

Really the code is the same in WPF and MAUI, just I instantiate an object of type ServiceClientGrpc, passing the same data. Is ServiceClientGrpc who creates the channel and call the service.

In the past, when I tried this with Xamarin, I read that there was limitations in Xamarin, but I though that now MAUI can use Net 6, I had hope that I could use the same library for all the clients.

Is it not possible to use the same library for windows clients and android clients?

Thanks.

Author: ComptonAlvaro
Assignees: -
Labels:

area-System.Net.Http

Milestone: -

@ghost
Copy link

ghost commented Aug 20, 2022

Tagging subscribers to 'arch-android': @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

Issue Details

I want to connect to a gRPC service that is hosted in ASP Core application. All my projects are .NET 6.

In the client side, I have a a library that is the gRPC client, that receive the certificates of the client in a string format and then it creates the channel and the connection to the service. In this way, I can use this library in many clients, WPF, MAUI and so on.

When I use the WPF client, it works, I can connect with the remote server, but when I use the MAUI project, I get an error. Is this error:


Exception of type 'Interop+AndroidCrypto+SslException' was thrown.

{Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__175`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.Http2Connection, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.HttpConnectionPool.GetHttp2ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.<RunCall>d__73[[GestorOrdenadores.Service.Client.Grpc.HelloRequest, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[GestorOrdenadores.Service.Client.Grpc.HelloReply, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in /_/src/Grpc.Net.Client/Internal/GrpcCall.cs:line 493")
   at GestorOrdenadores.Service.Client.Grpc.ServiceClientGrpc.SayHelloAsync(String paramNombre) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Service.Client.Grpc\ServiceClientGrpc.cs:line 71
   at GestorOrdenadores.Client.Maui.MainPage.OnBtnPingClicked(Object sender, EventArgs e) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Client.Maui\MainPage.xaml.cs:line 49}

The code of the library is this:

public class ServiceClientGrpc
    {
        #region constructores
        public ServiceClientGrpc(string paramDireccion, int paramPuerto, string paramCertificado, string paramKey)
        {
            var cert = X509Certificate2.CreateFromPem(paramCertificado, paramKey);

            paramDireccion = @"https://192.168.1.200";
            paramPuerto = 5001;


            HttpClientHandler miHttpHandler = new HttpClientHandler();
            miHttpHandler.ClientCertificates.Add(cert);
            HttpClient httpClient = new(miHttpHandler);

            miHttpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

            var channel = GrpcChannel.ForAddress(paramDireccion + ":" + paramPuerto, new GrpcChannelOptions
            {
                HttpClient = httpClient
            });





            _client = new Greeter.GreeterClient(channel);
        }
        #endregion constructores


        private readonly Greeter.GreeterClient _client;



        public async Task<string> SayHelloAsync(string paramNombre)
        {
            HelloRequest miRequest = new HelloRequest()
            {
                Name = paramNombre
            };

            return (await _client.SayHelloAsync(miRequest)).Message;
        }
    }

The WPF client:

private async void BtnSaludar_Click(object sender, RoutedEventArgs e)
{
    try
    {
        string miStrCertificado = System.IO.File.ReadAllText(@"Certificados\Client.crt");
        string miStrClave = System.IO.File.ReadAllText(@"Certificados\Client.key");

        ServiceClientGrpc miService = new ServiceClientGrpc("https://192.168.1.200", 5001, miStrCertificado, miStrClave);


        MessageBox.Show(await miService.SayHelloAsync("Álvaro"));
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The MAUI client:

private async void OnBtnSaludarClicked(object sender, EventArgs e)
{
    try
    {
        //Get address and port from XML file.
        string miStrDireccion = Configuracion.GestorConfiguracion.GetDireccionServicio();
        int miStrPuerto = Configuracion.GestorConfiguracion.GetPuertoServicio();

        //Get the certificates from Asserts files
        using Stream miStmCertificado = await FileSystem.OpenAppPackageFileAsync("certificados/client.crt");
        using StreamReader miSrCertificado = new StreamReader(miStmCertificado);
        string miStrCertificado = await miSrCertificado.ReadToEndAsync();

        using Stream miStmKey = await FileSystem.OpenAppPackageFileAsync("certificados/client.key");
        using StreamReader miSrKey = new StreamReader(miStmKey);
        string miStrKey = await miSrKey.ReadToEndAsync();


        ServiceClientGrpc miServicio = new ServiceClientGrpc(miStrDireccion, miStrPuerto, miStrCertificado, miStrKey);

        EtrRespuestaPing.Text = await miServicio.SayHelloAsync("Álvaro"); //Excetion in this line
    }
    catch(Exception ex)
    {
        await DisplayAlert("Alert", ex.Message, "OK");
    }
}

From the android device in which I am debugging the MAUI project, I can ping the server with an application that pings.

Really the code is the same in WPF and MAUI, just I instantiate an object of type ServiceClientGrpc, passing the same data. Is ServiceClientGrpc who creates the channel and call the service.

In the past, when I tried this with Xamarin, I read that there was limitations in Xamarin, but I though that now MAUI can use Net 6, I had hope that I could use the same library for all the clients.

Is it not possible to use the same library for windows clients and android clients?

Thanks.

Author: ComptonAlvaro
Assignees: -
Labels:

area-System.Net.Http, os-android, untriaged

Milestone: -

@ghost
Copy link

ghost commented Aug 20, 2022

Tagging subscribers to this area: @dotnet/ncl, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

I want to connect to a gRPC service that is hosted in ASP Core application. All my projects are .NET 6.

In the client side, I have a a library that is the gRPC client, that receive the certificates of the client in a string format and then it creates the channel and the connection to the service. In this way, I can use this library in many clients, WPF, MAUI and so on.

When I use the WPF client, it works, I can connect with the remote server, but when I use the MAUI project, I get an error. Is this error:


Exception of type 'Interop+AndroidCrypto+SslException' was thrown.

{Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__175`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.Http2Connection, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
   at System.Net.Http.HttpConnectionPool.GetHttp2ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.<RunCall>d__73[[GestorOrdenadores.Service.Client.Grpc.HelloRequest, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[GestorOrdenadores.Service.Client.Grpc.HelloReply, GestorOrdenadores.Service.Client.Grpc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in /_/src/Grpc.Net.Client/Internal/GrpcCall.cs:line 493")
   at GestorOrdenadores.Service.Client.Grpc.ServiceClientGrpc.SayHelloAsync(String paramNombre) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Service.Client.Grpc\ServiceClientGrpc.cs:line 71
   at GestorOrdenadores.Client.Maui.MainPage.OnBtnPingClicked(Object sender, EventArgs e) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Client.Maui\MainPage.xaml.cs:line 49}

The code of the library is this:

public class ServiceClientGrpc
    {
        #region constructores
        public ServiceClientGrpc(string paramDireccion, int paramPuerto, string paramCertificado, string paramKey)
        {
            var cert = X509Certificate2.CreateFromPem(paramCertificado, paramKey);

            paramDireccion = @"https://192.168.1.200";
            paramPuerto = 5001;


            HttpClientHandler miHttpHandler = new HttpClientHandler();
            miHttpHandler.ClientCertificates.Add(cert);
            HttpClient httpClient = new(miHttpHandler);

            miHttpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

            var channel = GrpcChannel.ForAddress(paramDireccion + ":" + paramPuerto, new GrpcChannelOptions
            {
                HttpClient = httpClient
            });





            _client = new Greeter.GreeterClient(channel);
        }
        #endregion constructores


        private readonly Greeter.GreeterClient _client;



        public async Task<string> SayHelloAsync(string paramNombre)
        {
            HelloRequest miRequest = new HelloRequest()
            {
                Name = paramNombre
            };

            return (await _client.SayHelloAsync(miRequest)).Message;
        }
    }

The WPF client:

private async void BtnSaludar_Click(object sender, RoutedEventArgs e)
{
    try
    {
        string miStrCertificado = System.IO.File.ReadAllText(@"Certificados\Client.crt");
        string miStrClave = System.IO.File.ReadAllText(@"Certificados\Client.key");

        ServiceClientGrpc miService = new ServiceClientGrpc("https://192.168.1.200", 5001, miStrCertificado, miStrClave);


        MessageBox.Show(await miService.SayHelloAsync("Álvaro"));
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The MAUI client:

private async void OnBtnSaludarClicked(object sender, EventArgs e)
{
    try
    {
        //Get address and port from XML file.
        string miStrDireccion = Configuracion.GestorConfiguracion.GetDireccionServicio();
        int miStrPuerto = Configuracion.GestorConfiguracion.GetPuertoServicio();

        //Get the certificates from Asserts files
        using Stream miStmCertificado = await FileSystem.OpenAppPackageFileAsync("certificados/client.crt");
        using StreamReader miSrCertificado = new StreamReader(miStmCertificado);
        string miStrCertificado = await miSrCertificado.ReadToEndAsync();

        using Stream miStmKey = await FileSystem.OpenAppPackageFileAsync("certificados/client.key");
        using StreamReader miSrKey = new StreamReader(miStmKey);
        string miStrKey = await miSrKey.ReadToEndAsync();


        ServiceClientGrpc miServicio = new ServiceClientGrpc(miStrDireccion, miStrPuerto, miStrCertificado, miStrKey);

        EtrRespuestaPing.Text = await miServicio.SayHelloAsync("Álvaro"); //Excetion in this line
    }
    catch(Exception ex)
    {
        await DisplayAlert("Alert", ex.Message, "OK");
    }
}

From the android device in which I am debugging the MAUI project, I can ping the server with an application that pings.

Really the code is the same in WPF and MAUI, just I instantiate an object of type ServiceClientGrpc, passing the same data. Is ServiceClientGrpc who creates the channel and call the service.

In the past, when I tried this with Xamarin, I read that there was limitations in Xamarin, but I though that now MAUI can use Net 6, I had hope that I could use the same library for all the clients.

Is it not possible to use the same library for windows clients and android clients?

Thanks.

Author: ComptonAlvaro
Assignees: -
Labels:

area-System.Net.Http, area-System.Net.Security, os-android, untriaged

Milestone: -

@wfurt
Copy link
Member

wfurt commented Aug 20, 2022

cc: @simonrozsival

@simonrozsival
Copy link
Member

@ComptonAlvaro Unfortunately, there isn't 1:1 feature parity when it comes to bypassing validation of self-signed certificates between .NET on Android and on Windows, so using miHttpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; or miHttpHandler.ClientCertificates.Add(cert); won't bypass Android's internal validation in your app.

There should be a workaround though. Since you have the certificate files inside of your app, you should be able to add network_security_config.xml to your Android resources, reference it from AndroidManifest.xml, and import your self-signed certificates this way. This documentation page should give you all the information necessary: https://developer.android.com/training/articles/security-config

Please let me know if this resolves this issue for you or if you have any follow-up questions.

@ComptonAlvaro
Copy link
Author

ComptonAlvaro commented Aug 22, 2022

@simonrozsival Well, if I understand right, the problem is if I want to use ServerCertificateCustomValidationCallback, that I can't use it in android.

Really I would like to avoid this in windows, but I don't know how to indicate the CA when I create the X509 certificate. There is more details of this here: #74250.

If I could create a X509 certificate in which I could I could indicate the CA and the client certificates, I guess it would be a possible good solution.

In the past, when I did some tries with Xamarin and gRPC, using Gprc.Core, I could create a SslCredentials indicating the CA and the client certificates in this way:

private Gestor.GestorClient GetClient()
{
    Environment.SetEnvironmentVariable("GRPC_VERBOSITY", "DEBUG");

    var keypair = new KeyCertificatePair(_clientCertificate, _clientPrivateKey);
    var sslCreds = new SslCredentials(_caCertificate, keypair);

    var channel = new Channel(_serviceAddress, _port, sslCreds);
    var client = new Gestor.GestorClient(channel);

    return client;
}

First I create a KeyCertificatePair with the client certificates and then I create a SslCredentials with this pair and the CA.

But this is with native gRPC.Core library, I I want to use gRPC.Net, with the command ForAddress(), but I don't have the option to set the CA, only to use the X509 certificate, and the X509 certificate has not accept the CA.

So, is it possible to create a X509 certificate with the CA? Or it is possible to create a channel with gRPC.Net and ForAddress() to indicate the CA? If I could do that, I get it would be easier to have only one library with the gRPC service and be shared in Android and Windows clients, so I don't need to have different configurations for each client.

Really in fact, my problems is with the WPF client, that I really I would prefer to can use the CA and not set ServerCertificateCustomValidationCallback, because I have the CA in which I want to trust.

Just this is a thought or question, but I will check the possibiblity to configure Android in the way that you suggest me. But I guess that it wouldn't allow me to have one client library for all the clients, no matter if it is Android or Windows.

@simonrozsival
Copy link
Member

@ComptonAlvaro yes, you can't use ServerCertificateCustomValidationCallback on Android for this use case. The only thing you should need on Android is the network_seurity_config.xml. I think you can still have one ServiceClientGrpc class for all platforms, just consider having different constructors for different platforms.

I won't be able to help you with the WPF app so either somebody will help you resolve this problem in #74250 or consider opening a separate issue with the Windows specifics.

@ComptonAlvaro
Copy link
Author

ComptonAlvaro commented Aug 22, 2022

Well, I am trying to use in the Grpc.Core instead of Grpc.Net, because Grpc.Core allows me to create a channel using the credentials in which I can set the certificates of the client and the CA.

In this case, the code of the common library is this:

using Grpc.Core;
//using Grpc.Net.Client;
//using System.Threading.Channels;

.....


public ServiceClientGrpc(string paramDireccion, int paramPuerto, string paramCA, string paramCertificado, string paramKey)
{
    var keypair = new KeyCertificatePair(paramCertificado, paramKey);
    var sslCreds = new SslCredentials(paramCA, keypair);

    Channel channel = new Channel(paramDireccion, paramPuerto, sslCreds); //Error in MAUI but not in WPF
    _client = new Greeter.GreeterClient(channel);
}

Now it works if I use the WPF application. But I am still getting an error in the MAUI application. In this case it is a different error.

{System.DllNotFoundException: grpc_csharp_ext
   at Grpc.Core.Internal.NativeLogRedirector.Redirect(NativeMethods native) in /var/local/git/grpc/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs:line 49
   at Grpc.Core.Internal.NativeExtension..ctor() in /var/local/git/grpc/src/csharp/Grpc.Core/Internal/NativeExtension.cs:line 46
   at Grpc.Core.Internal.NativeExtension.Get() in /var/local/git/grpc/src/csharp/Grpc.Core/Internal/NativeExtension.cs:line 67
   at Grpc.Core.Internal.NativeMethods.Get() in /var/local/git/grpc/src/csharp/Grpc.Core/Internal/NativeMethods.cs:line 49
   at Grpc.Core.GrpcEnvironment.GrpcNativeInit() in /var/local/git/grpc/src/csharp/Grpc.Core/GrpcEnvironment.cs:line 373
   at Grpc.Core.GrpcEnvironment..ctor() in /var/local/git/grpc/src/csharp/Grpc.Core/GrpcEnvironment.cs:line 302
   at Grpc.Core.GrpcEnvironment.AddRef() in /var/local/git/grpc/src/csharp/Grpc.Core/GrpcEnvironment.cs:line 78
   at Grpc.Core.Channel..ctor(String target, ChannelCredentials credentials, IEnumerable`1 options) in /var/local/git/grpc/src/csharp/Grpc.Core/Channel.cs:line 70
   at Grpc.Core.Channel..ctor(String host, Int32 port, ChannelCredentials credentials, IEnumerable`1 options) in /var/local/git/grpc/src/csharp/Grpc.Core/Channel.cs:line 107
   at Grpc.Core.Channel..ctor(String host, Int32 port, ChannelCredentials credentials) in /var/local/git/grpc/src/csharp/Grpc.Core/Channel.cs:line 95
   at GestorOrdenadores.Service.Client.Grpc.ServiceClientGrpc..ctor(String paramDireccion, Int32 paramPuerto, String paramCA, String paramCertificado, String paramKey) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Service.Client.Grpc\ServiceClientGrpc.cs:line 74
   at GestorOrdenadores.Client.Maui.MainPage.OnBtnPingClicked(Object sender, EventArgs e) in D:\desarrollo\proyectos\GestorOrdenadores\GestorOrdenadores.Client.Maui\MainPage.xaml.cs:line 51}

According to the first lines of the error, it seems it is related with the native methods in Grpc.Core.

Really I don't know the difference between Grpc.Core and Grpc.Net and which are the advantages of Grpc.Net, but if this solution could work, I guess it is more simple, I could avoid the needed to have different constructors in my library according to the platform and I could avoid to configure the android project with the network_seurity_config.xml. So the solution would be simplier and more transparent for consumers of the library, you just need to five the CA and the certificates.

@wfurt
Copy link
Member

wfurt commented Aug 22, 2022

BTW should add some documentation about the disparity @simonrozsival?

It seems like you are missing some dependencies for the Grpc.Core @ComptonAlvaro
cc: @JamesNK for any insight on gRPC.

@steveisok steveisok removed the untriaged New issue has not been triaged by the area owner label Aug 22, 2022
@simonrozsival
Copy link
Member

@wfurt the documentation of the changes in the .NET 6 Android crytpo is definitely lacking. Since MAUI was released, there have been quite a few customers migrating from legacy Xamarin.Android who are experiencing similar issues. The gRPC troubleshooting docs should definitely contain some mention of the Android specifics. We should also add some general HttpClientHandler.ServerCertificateCustomValidationCallback remarks for Android but I'm not sure where the best place is - the HttpClientHandler class docs I assume, although there isn't any other platform specific information, so I'm there doesn't seem to be a precedent.

@JamesNK
Copy link
Member

JamesNK commented Aug 23, 2022

Grpc.Core isn't supported by Microsoft. It was created by the gRPC team. @ComptonAlvaro if you are using Grpc.Core then please ask questions for using it at grpc/grpc. No one at dotnet/runtime knows how it works.

Grpc.Net.Client currently doesn't officially support MAUI+Android. Waiting on work in #69095 before committing to supporting gRPC on Android.

In saying that, I'm pretty sure Grpc.Net.Client requires SocketsHttpHandler. That means either:

  • Creating SocketsHttpHandler directly
  • Or using HttpClientHandler + configuration to ensure it creates SocketsHttpHandler internally (I don't know the config name off the top of my head).

When Android is supported, then I agree that gRPC+Android docs are required. There might also be the opportunity to detect at runtime that the current platform is Android and check that a valid handler is configured.

@JamesNK
Copy link
Member

JamesNK commented Aug 23, 2022

A recurring theme of gRPC + Android is TLS issues. I've mentioned this before, but I think SslClientAuthenticationOptions.RemoteCertificateValidationCallback needs to be supported.

   var sslOptions = new SslClientAuthenticationOptions
   {
      // Leave certs unvalidated for debugging
      RemoteCertificateValidationCallback = delegate { return true; },
   };

   var handler = new SocketsHttpHandler()
   {
      SslOptions = sslOptions,
   };

@ComptonAlvaro
Copy link
Author

@simonrozsival I am trying the solution that you suggest using the network_security_config.xml file configuration. But by the moment I am having some problem.

I have added the network_security_config.xml file in the folder platform\android\resources\xml and I add the parameter android:networkSecurityConfig="@xml/network_security_config" in the androidmanifest.xml file.

The code is:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"
               android:networkSecurityConfig="@xml/network_security_config"></application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>

The network_security_config.xml has this code:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config>
    <trust-anchors>
      <certificates src="@raw/ca"/>
    </trust-anchors>
  </domain-config>
</network-security-config>

But when I compile, I get an error that says that it is not possible to fin the ca in the path resources/raw/ca. The ca.crt is in the folder [MauiProject]\Resources\raw\ca.crt and in properties I tried with MauiAsset and AndroidAsset.

So I am still trying to see how I could compile the project.

@simonrozsival
Copy link
Member

@ComptonAlvaro I believe the ca.crt belongs into platform\android\resources\raw. Also, I think you are missing <domain>your.domain.com</domain> in <domain-config>.

@ComptonAlvaro
Copy link
Author

@simonrozsival I have created the folder platform\android\resourecs\raw and put there the certificate and still I get the same error.

I quit the domain because I don't have a domain, it is just a computer, perhaps I should to put the IP of the server. If this is a problem, I could solve after seeing why I get this error with the certificate.

I have tried to delete and it compiles.

@simonrozsival
Copy link
Member

@ComptonAlvaro you'll either need to put the IP address there or use <base-config> instead of <domain-config>. I checked my MAUI project where I tested importing CA certificate through network_security_config.xml and the file structure looks this:

/Platforms/Android/Resources
    /raw/ca.pem
    /xml/network_security_config.xml
    /AndroidManifest.xml
    ...
/MauiProgram.cs
...

@ComptonAlvaro
Copy link
Author

ComptonAlvaro commented Aug 23, 2022

@simonrozsival In summary, now I have this:

imagen

My network_security_config.xml file:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config>
    <domain includeSubdomains="true">192.168.1.200</domain>
    <trust-anchors>
      <certificates src="@raw/ca"/>
    </trust-anchors>
  </domain-config>
</network-security-config>

I have tried with ca.pem but it can't find anyway.

My AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"
               android:networkSecurityConfig="@xml/network_security_config"></application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>

@simonrozsival
Copy link
Member

@ComptonAlvaro Apparently the network_security_config.xml is bundled correctly but it seems that ca.pem isn't. That could mean that the project configuration is incorrect. Do you have something like this in your .csproj file?

<ItemGroup>
	<None Update="Platforms\Android\Resources\raw\ca.pem">
		<CopyToOutputDirectory>Always</CopyToOutputDirectory>
	</None>
	<None Update="Platforms\Android\Resources\xml\network_security_config.xml">
		<CopyToOutputDirectory>Always</CopyToOutputDirectory>
	</None>
</ItemGroup>

@ComptonAlvaro
Copy link
Author

This is my .csproj project:

<ItemGroup>
		<!-- App Icon -->
		<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />

		<!-- Splash Screen -->
		<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

		<!-- Images -->
		<MauiImage Include="Resources\Images\*" />
		<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />

		<!-- Custom Fonts -->
		<MauiFont Include="Resources\Fonts\*" />

		<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
		<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
	</ItemGroup>

	<ItemGroup>
	  <AndroidResource Remove="Platforms\Android\Resources\raw\ca.pem" />
	</ItemGroup>

	<ItemGroup>
	  <None Remove="Platforms\Android\Resources\raw\ca.pem" />
	  <None Remove="Platforms\Android\Resources\xml\network_security_config.xml" />
	  <None Remove="Resources\Raw\ca" />
	  <None Remove="Resources\Raw\certificados\ca.crt" />
	  <None Remove="Resources\Raw\certificados\client.crt" />
	  <None Remove="Resources\Raw\certificados\client.key" />
	  <None Remove="Resources\Raw\configuracion.xml" />
	</ItemGroup>

	<ItemGroup>
	  <ProjectReference Include="..\GestorOrdenadores.Service.Client.Contratos\GestorOrdenadores.Service.Client.Contratos.csproj" />
	  <ProjectReference Include="..\GestorOrdenadores.Service.Client.Grpc\GestorOrdenadores.Service.Client.Grpc.csproj" />
	</ItemGroup>

	<ItemGroup>
	  <MauiAsset Update="Resources\Raw\configuracion.xml">
	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
	  </MauiAsset>
	</ItemGroup>

	<ItemGroup>
	  <MauiXaml Update="ConfiguracionPage.xaml">
	    <Generator>MSBuild:Compile</Generator>
	  </MauiXaml>
	</ItemGroup>

	<ItemGroup>
	  <MauiAsset Update="Resources\Raw\certificados\ca.crt">
	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
	  </MauiAsset>
	  <MauiAsset Update="Resources\Raw\certificados\client.crt">
	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
	  </MauiAsset>
	  <MauiAsset Update="Resources\Raw\certificados\client.key">
	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
	  </MauiAsset>
	</ItemGroup>

	<ItemGroup>
	  <MauiAsset Include="Platforms\Android\Resources\raw\ca.pem">
	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
	    <LogicalName>%(RecursiveDir)%(Filename)%(Extension)</LogicalName>
	  </MauiAsset>
	</ItemGroup>

The ca.pem is setted as remove instead of copy to output directory.

@ComptonAlvaro
Copy link
Author

@simonrozsival Thanks so much, I finally get it works using the option of network_security_config.xml.

However, I think this have some drawbacks.

For example, if I want to update the CA, I have to build a new version of the application. However, in the WPF version, I can use the ca.crt, load it as string and pass it has paramter to the method that validates the server certificate. So I don't need to build a new versión, just to have an option in the application to update or load a new CA.

Also it would be nice if I could use the same code in MAUI than in WPF, not need to do aditional configuration in android, so it would be easier to maintenance.

But at least it works and it is not so serious, the CA shouldn't change often but it is not needed to install the CA in android trusted certificates, so it is transparent fot the user, it has only to update the application.

Thanks so much.

@simonrozsival
Copy link
Member

@ComptonAlvaro I understand your comments. There are limitations on Android, the list of unsupported functionality is tracked here: #45741 (incl. HttpClientHandler.ServerCertificateCustomValidationCallback).

I'm glad you got your app to work :) I'll close the issue now.

@karelz karelz added this to the 8.0.0 milestone Aug 25, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants