dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.25k stars 4.73k forks source link

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

Closed ComptonAlvaro closed 2 years ago

ComptonAlvaro commented 2 years ago

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 commented 2 years ago

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.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.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.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Grpc.Net.Client.Internal.GrpcCall`2.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 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 commented 2 years ago

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.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.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.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Grpc.Net.Client.Internal.GrpcCall`2.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 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 commented 2 years ago

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.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.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.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Grpc.Net.Client.Internal.GrpcCall`2.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 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 commented 2 years ago

cc: @simonrozsival

simonrozsival commented 2 years ago

@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 commented 2 years ago

@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: https://github.com/dotnet/runtime/issues/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 commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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.

simonrozsival commented 2 years ago

@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 commented 2 years ago

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 https://github.com/dotnet/runtime/issues/69095 before committing to supporting gRPC on Android.

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

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 commented 2 years ago

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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

@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 commented 2 years ago

@ComptonAlvaro I understand your comments. There are limitations on Android, the list of unsupported functionality is tracked here: https://github.com/dotnet/runtime/issues/45741 (incl. HttpClientHandler.ServerCertificateCustomValidationCallback).

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