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

[Bug] SSL Connection refused when calling ASP.NET Core API endpoints from .NET MAUI App on Android #62966

Closed
TanvirArjel opened this issue Jul 16, 2021 · 49 comments

Comments

@TanvirArjel
Copy link

The ASP.NET Core Web API app is up and running. Making requests to the API endpoints from Blazor Web Assembly works perfectly fine. Whenever making request from the .NET MAUI Blazor App it's throwing the following exception:

[DOTNET] System.Net.Http.HttpRequestException: Connection refused (localhost:44363)
[DOTNET]  ---> System.Net.Sockets.SocketException (111): Connection refused
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken 
[DOTNET] cancellationToken) in System.Net.Sockets.dll:token 0x60002b9+0x1f
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) in System.Net.Sockets.dll:token 0x60002b3+0x26
[DOTNET]    at Syst
[DOTNET] em.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) in System.Net.Sockets.dll:token 0x600029c+0xa4
[DOTNET]    at System.Net.Http.HttpConnectionP
[DOTNET] ool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x1b4
[DOTNET]    --- End of inner exception stack trace ---
[DOTNET]    at System.Net.Http.HttpC
[DOTNET] onnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x298
[DOTNET]    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpReque
[DOTNET] stMessage request, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006ff+0x14d
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellati
[DOTNET] onToken) in System.Net.Http.dll:token 0x6000701+0x8f
[DOTNET]    at System.Net.Http.HttpConnectionPool.SendUsingHttp11Async(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f1
[DOTNET] +0x15d
[DOTNET]    at System.Net.Http.HttpConnectionPool.DetermineVersionAndSendAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f2+0x224
[DOTNET]    at System.Net.Http.HttpConne
[DOTNET] ctionPool.SendAndProcessAltSvcAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f3+0x8e
[DOTNET] ssage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f4+0x99
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequ07-16 12:39:49.024 I/DOTNET  (16085):    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationT
[DOTNET] oken) in System.Net.Http.dll:token 0x6000787+0x94
[DOTNET]    at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c3+0xd7
[DOTNET]    at MauiB
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\AuthorizationDelegatingHandler.cs:line 43
[DOTNET]    at Microsoft.E
[DOTNET] xtensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c8+0xef
[DOTNET] ge request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) in System.Net.Http.dll:token 0x600022f+0x1b7
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\Authoriz07-16 12:39:49.025 I/DOTNET  (16085):    at MauiBlazor.Share
[DOTNET] d.Services.UserService.LoginAsync(LoginModel loginModel) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Services\UserService.cs:line 132
[DOTNET]    at MauiBlazorApp.Components.IdentityComponents.LoginComponent.HandleValidSubmitAsync() in D:\GitHub
[DOTNET] \CleanArchitecture\src\ClientApps\MauiBlazorApp\MauiBlazorApp\Components\IdentityComponents\LoginComponent.razor.cs:line 51

There is no CORS issue because the API app is allowing any origin as follows:

services.AddCors(options =>
{
    options.AddPolicy(myAllowSpecificOrigins, builder =>
    {
        builder
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowed(_ => true);
    });
});

Please help me how can I overcome the issue so that I can make requests to the ASP.NET Core REST endpoints from .NET MAUI Blazor app successfully. Thanks in advance.

@Eilon
Copy link
Member

Eilon commented Jul 19, 2021

Hi @TanvirArjel can you share more info about this setup? Is it all on Windows, with .NET MAUI targeting WinUI, and the ASP.NET Core server is running locally on the same Windows machine?

@TanvirArjel
Copy link
Author

TanvirArjel commented Jul 19, 2021

@Eilon Here is the complete repo: https://github.com/TanvirArjel/CleanArchitecture

Is it all on Windows, with .NET MAUI targeting WinUI, and the ASP.NET Core server is running locally on the same Windows machine?

Yes! It's on Windows. Both servers are running on Windows local machine.

@Eilon
Copy link
Member

Eilon commented Jul 19, 2021

BTW CORS shouldn't matter here because CORS applies only to browsers making calls to a remote server. In this case it's a .NET Core app making a call, so CORS isn't a factor one way or another.

@Eilon
Copy link
Member

Eilon commented Jul 19, 2021

The first thing that I can think of is some issue related to HTTP vs. HTTPS. Is everything consistently using HTTPS in this scenario?

@TanvirArjel
Copy link
Author

@Eilon

Is everything consistently using HTTPS in this scenario?

I think so. It would be great if you run the repository on your machine and test.

@Eilon
Copy link
Member

Eilon commented Jul 19, 2021

@TanvirArjel in that case it will likely take a while for me to get to this because it could involve a lot of investigation. Ideally we have minimal repro projects for this so there are fewer possible problems.

@TanvirArjel
Copy link
Author

@Eilon Okay! I shall provide a minimal repo soon.

@TanvirArjel
Copy link
Author

@Eilon Here is the minimal repo to reproduce the issue: https://github.com/TanvirArjel/MauiDemo. After running both API and MAUI Blazor app in debug mode, please click on the FetchData menu and see the debug output.

This has been an irritating issue as this blocked my MAUI learning. Please let me know the update.

Thanks in advance.

@TanvirArjel
Copy link
Author

@Eilon Could you please give me an update on this issue?

Thank you.

@Eilon
Copy link
Member

Eilon commented Aug 19, 2021

Hi @TanvirArjel I updated your project to be compatible with preview7/RC1 and it seems to work just fine for me. First I started the ASP.NET Core server so that the API would run on port 44393 (using HTTPS), then I deployed the WinUI project and ran it from the Start Menu (there's a bug right now in VS where you can't run the WinUI app directly), and it was able to do the HttpClient call to get the weather forecast.

image

You can see the changes I made to the app here: TanvirArjel/MauiDemo#1

(I only changed things that changed in MAUI/Blazor/WinUI, I didn't change any app code.)

@TanvirArjel
Copy link
Author

@Eilon Great to hear that. I shall test the same project and get back to you.

Thank you again.

@Eilon
Copy link
Member

Eilon commented Aug 19, 2021

Do you know if you have the ASP.NET Core HTTPS developer certificate installed?

Check out this post from Scott Hanselman: https://www.hanselman.com/blog/developing-locally-with-aspnet-core-under-https-ssl-and-selfsigned-certs

You might need to run this command line tool: dotnet dev-certs https --trust

@TanvirArjel
Copy link
Author

TanvirArjel commented Aug 20, 2021

@Eilon I see, you have only tested with MAUI Blazor Windows UI and you did not check with MAUI Blazor Android app. For windows UI its working fine but for the MAUI Blazor Android it's not working and throwing the same error:

image

@TanvirArjel
Copy link
Author

@Eilon Please keep me updated after testing on your machine. This is a very crucial issue which blocked my MAUI development.

@Eilon
Copy link
Member

Eilon commented Aug 20, 2021

Oh, I see. There's probably something that needs to be done in Android to make the cert trusted. The cert trust is done on the client, so when you run the command line tool, that makes Windows trust it. But Android is using a different cert store.

@Eilon
Copy link
Member

Eilon commented Aug 20, 2021

OK I found the doc that discusses how to do this: https://docs.microsoft.com/xamarin/cross-platform/deploy-test/connect-to-local-web-services#bypass-the-certificate-security-check

You can do one of these:

  1. Use HTTP (instead of HTTPS) during dev and local debugging, so there's no HTTPS certificate needed at all
  2. Follow the steps in that doc to skip cert checking on HTTPS in your Android and iOS apps
  3. Use an HTTPS cert that is already trusted (I think LetsEncrypt might work for this?)

@TanvirArjel
Copy link
Author

TanvirArjel commented Aug 20, 2021

@Eilon Thank you so much. Could you please update the demo project with option-2.

Thanks in advance.

@TanvirArjel
Copy link
Author

@Eilon Is there any documentation on how to use LetsEncrypt in MAUI?

@Eilon
Copy link
Member

Eilon commented Aug 20, 2021

The docs I pointed to include code samples for how to do option 2. If that doesn't work, please let me know.

I'm not sure of docs for LetsEncrypt, but it's a matter of using a LetsEncrypt cert in the ASP.NET Core app for SSL. Then the Android app I think would automatically trust it, because the CA for LetsEncrypt is I think already trusted in Android. (This is far outside my area of expertise, so I'm not 100% on this.)

@TanvirArjel
Copy link
Author

TanvirArjel commented Aug 21, 2021

@Eilon Here is the test result:

  1. Use HTTP (instead of HTTPS) during dev and local debugging, so there's no HTTPS certificate needed at all

I have tried. Only works for Windows Ui but does not work for mobile apps and shows the same error.

  1. Follow the steps in that doc to skip cert checking on HTTPS in your Android and iOS apps

I have tried as follows:

services.AddHttpClient("WeatherApi", c =>
{
    c.BaseAddress = new Uri("https://localhost:44393/");
    c.DefaultRequestHeaders.Add("Accept", "application/json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
}).ConfigurePrimaryHttpMessageHandler(x => new HttpClientHandler()
{
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        if (cert.Issuer.Equals("CN=localhost"))
        {
            return true;
        }

        return errors == System.Net.Security.SslPolicyErrors.None;
    }
});

only works for Windows Ui but does not work for mobile apps and shows the following error:

[chromium] [INFO:CONSOLE(1)] "Operation is not supported on this platform.
[chromium]    at System.Net.Http.HttpClientHandler.set_ServerCertificateCustomValidationCallback(Func`5 value) in System.Net.Http.dll:token 0x6000252+0x0
[chromium]    at MauiBlazorApp.Startup.<>c.<Configure>b__0_3(IServiceProvider x) in D:\MauiDemo\MauiBlazorApp\MauiBlazorApp\Startup.cs:line 37
[chromium]    at Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.<>c__DisplayClass6_0.<ConfigurePrimaryHttpMessageHandler>b__1(HttpMessageHandlerBuilder b) in Microsoft.Extensions.Http.dll:token 0x60000e6+0xd
[chromium]    at Microsoft.Extensions.Http.DefaultHttpClientFactory.<>c__DisplayClass17_0.<CreateHandlerEntry>g__Configure|0(HttpMessageHandlerBuilder b) in Microsoft.Extensions.Http.dll:token 0x6000110+0x15
[chromium]    at Microsoft.Extensions.Http.LoggingHttpMessageHandlerBuilderFilter.<>c__DisplayClass3_0.<Configure>b__0(HttpMessageHandlerBuilder builder) in Microsoft.Extensions.Http.dll:token 0x600011b+0x0
[chromium]    at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateHandlerEntry(String name) in Microsoft.Extensions.Http.dll:token 0x6000088+0xc0
[chromium]    at Microsoft.Extensions.Http.DefaultHttpClientFactory.<>c__DisplayClass14_0.<.ctor>b__1() in Microsoft.Extensions.Http.dll:token 0x600010e+0x0
[chromium]    at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ViaFactory(LazyThreadSafetyMode mode) in System.Private.CoreLib.dll:token 0x600125c+0x43
[chromium]    at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) in System.Private.CoreLib.dll:token 0x600125d+0x22
[chromium]    at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].CreateValue() in System.Private.CoreLib.dll:token 0x6001262+0x74
[chromium]    at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].get_Value() in System.Private.CoreLib.dll:token 0x6001268+0xa
[chromium]    at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateHandler(String name) in Microsoft.Extensions.Http.dll:token 0x6000087+0x20
[chromium]    at Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateClient(String name) in Microsoft.Extensions.Http.dll:token 0x6000086+0xe
[chromium]    at MauiBlazorApp.Data.WeatherForecastService..ctor(IHttpClientFactory httpClientFactory) in D:\MauiDemo\MauiBlazorApp\MauiBlazorApp\Data\WeatherForecastService.cs:line 14
[chromium]    at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, Object[] parameters, Boolean wrapExceptions) in System.Private.CoreLib.dll:token 0x6004f5b+0x20
[chromium]    at System.Reflection.RuntimeConstructorInfo.DoInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in System.Private.CoreLib.dll:token 0x6004f5a+0x86
[chromium]    at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in System.Private.CoreLib.dll:token 0x6004f5c+0x0
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) in Microsoft.Extensions.DependencyInjection.dll:token 0x600008d+0x45
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument) in Microsoft.Extensions.DependencyInjection.dll:token 0x60000a6+0x3f
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context) in Microsoft.Extensions.DependencyInjection.dll:token 0x600008e+0x61
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument) in Microsoft.Extensions.DependencyInjection.dll:token 0x60000a5+0x4e
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) in Microsoft.Extensions.DependencyInjection.dll:token 0x600008b+0x26
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType) in Microsoft.Extensions.DependencyInjection.dll:token 0x6000068+0x4b
[chromium]    at System.Collections.Concurrent.ConcurrentDictionary`2[[System.Type, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Func`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope, Microsoft.Extensions.DependencyInjection, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetOrAdd(Type key, Func`2 valueFactory) in System.Collections.Concurrent.dll:token 0x60000d2+0x4f
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) in Microsoft.Extensions.DependencyInjection.dll:token 0x6000066+0xd
[chromium]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) in Microsoft.Extensions.DependencyInjection.dll:token 0x600010b+0x13
[chromium]    at Microsoft.AspNetCore.Components.ComponentFactory.<>c__DisplayClass7_0.<CreateInitializer>g__Initialize|1(IServiceProvider serviceProvider, IComponent component) in Microsoft.AspNetCore.Components.dll:token 0x60003c5+0x18
[chromium]    at Microsoft.AspNetCore.Components.ComponentFactory.PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance) in Microsoft.AspNetCore.Components.dll:token 0x60000ac+0x2d
[chromium]    at Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType) in Microsoft.AspNetCore.Components.dll:token 0x60000ab+0x2b
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateComponent(Type componentType) in Microsoft.AspNetCore.Components.dll:token 0x6000268+0x0
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame& frame, Int32 parentComponentId) in Microsoft.AspNetCore.Components.dll:token 0x6000274+0x69
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex) in Microsoft.AspNetCore.Components.dll:token 0x60002a3+0x29
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex) in Microsoft.AspNetCore.Components.dll:token 0x60002a2+0x49
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex) in Microsoft.AspNetCore.Components.dll:token 0x600029e+0x74
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) in Microsoft.AspNetCore.Components.dll:token 0x600029c+0x21c
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) in Microsoft.AspNetCore.Components.dll:token 0x6000294+0x22b
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) in Microsoft.AspNetCore.Components.dll:token 0x600029c+0x1d1
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) in Microsoft.AspNetCore.Components.dll:token 0x6000294+0x22b
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) in Microsoft.AspNetCore.Components.dll:token 0x600029c+0x197
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) in Microsoft.AspNetCore.Components.dll:token 0x6000294+0x22b
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) in Microsoft.AspNetCore.Components.dll:token 0x600029c+0x197
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) in Microsoft.AspNetCore.Components.dll:token 0x6000294+0x22b
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree) in Microsoft.AspNetCore.Components.dll:token 0x6000292+0x25
[chromium]    at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException) in Microsoft.AspNetCore.Components.dll:token 0x600030a+0x7e
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry) in Microsoft.AspNetCore.Components.dll:token 0x6000282+0x13
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue() in Microsoft.AspNetCore.Components.dll:token 0x600027e+0x6d", source: https://0.0.0.0/_framework/blazor.webview.js (1)

@TanvirArjel
Copy link
Author

@Eilon Hope you are doing fine. I am waiting to hear from you as my development struck here.

@Eilon
Copy link
Member

Eilon commented Aug 25, 2021

@TanvirArjel
Copy link
Author

TanvirArjel commented Aug 26, 2021

@Eilon Now it's showing the following error:

[INFO:CONSOLE(1)] "Response status code does not indicate success: 400 (Bad Request).
[chromium]    at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() in System.Net.Http.dll:token 0x6000358+0x39
[chromium]    at System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1[[MauiBlazorApp.Data.WeatherForecast[], MauiBlazorApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in System.Net.Http.Json.dll:token 0x6000030+0x80
[chromium]    at MauiBlazorApp.Data.WeatherForecastService.GetForecastAsync(DateTime startDate) in D:\MauiDemo\MauiBlazorApp\MauiBlazorApp\Data\WeatherForecastService.cs:line 19
[chromium]    at MauiBlazorApp.Pages.FetchData.OnInitializedAsync() in D:\MauiDemo\MauiBlazorApp\MauiBlazorApp\Pages\FetchData.razor.cs:line 17
[chromium]    at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync() in Microsoft.AspNetCore.Components.dll:token 0x60000a3+0xbe
[chromium]    at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState) in Microsoft.AspNetCore.Components.dll:token 0x6000285+0x65", source: https://0.0.0.0/_framework/blazor.webview.js (1)

Although it is working fine for windows UI. I am done and dusted.

Could you please run the demo app and make it workable: https://github.com/TanvirArjel/MauiDemo

@AmSmart
Copy link

AmSmart commented Aug 27, 2021

Well, the above error is simply an HTTP Bad Request(400) which either means you are calling the HTTP endpoint wrongly or something is messed up on your server. Not really a MAUI issue anymore at this point.

Is there any documentation on how to use LetsEncrypt in MAUI?

Concerning this, I'd like to point out that Let's Encrypt is a Certificate Authority that helps sign TLS (popularly called SSL) certificates so that your HTTPS connection can be trusted. That being said Let's Encrypt interfaces with the ASP.NET Core part of the app and not MAUI. For development scenarios, dotnet helps provide locally trusted self-signed certificates on Windows and macOS using the command @Eilon provided earlier.

If however, you want to deploy your ASP.NET Core web app to production, Digital Ocean has a very nice blog on using Let's Encrypt with ASP.NET Core and Nginx as a reverse proxy. If you want to deploy directly to Kestrel, you can follow guides from this dev.to blog.

I hope all of these helps you understand the whole thing better. I think you should close the issue as well, as the initial error wasn't a bug but a feature.

Happy Coding!

@TanvirArjel
Copy link
Author

@AmSmart

Well, the above error is simply an HTTP Bad Request(400) which either means you are calling the HTTP endpoint wrongly or something is messed up on your server. Not really a MAUI issue anymore at this point.

I know this is a simple HTTP 400 error but this is not the case here. The same code work MAUI Windows UI. It requires an extra bit of configuration for MAUI Android.

If you think is plain and simple HTTP 400 then please get the code from here (https://github.com/TanvirArjel/MauiDemo) and run and find the issue, please.

@AmSmart
Copy link

AmSmart commented Aug 29, 2021

I don't have the VS2022 preview installed and I don't plan to install it soon, hence I can't really run MAUI apps yet. Since you're getting a 400, I'd advise you to debug it on the Server Side (Weather App), find out the content of the request that was sent from the Android app that led to a server-side error. That'll give more clarity to discovering the bug.

Happy Coding!

@Eilon
Copy link
Member

Eilon commented Sep 7, 2021

@TanvirArjel if you set up verbose logging on the ASP.NET Core app, can you see the request coming in from the Android emulator? As @AmSmart says, the fact that it's an HTTP 400 implies that the server is receiving the HTTP request, but thinks that the client sent a bad request. If you can see from the server what it thinks the request is or what's wrong with it, that could lead us to the answer.

@TanvirArjel
Copy link
Author

@Eilon I shall give try once .NET 6.0 RC-1 is released. .NET 6.0 preview 7 has so many issues.

@Eilon
Copy link
Member

Eilon commented Sep 11, 2021

Sounds good, it should be out very soon!

@TanvirArjel
Copy link
Author

@Eilon, I have tested again after updating to .NET 6.0 RC 1. The same old error. I have enabled log level to Trace for the API app but does not show anything.

I am requesting you run this (https://github.com/TanvirArjel/MauiDemo) minimal repo on your machine and identify the issue.

Note: Request to the API app from every other source like POSTMAN, Fiddler, Windows App works perfectly fine but don't work from the MAUI android app.

I am pretty sure this is related to the IIS Express and android emulator config issue.

@ghost
Copy link

ghost commented Dec 17, 2021

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

Issue Details

The ASP.NET Core Web API app is up and running. Making requests to the API endpoints from Blazor Web Assembly works perfectly fine. Whenever making request from the .NET MAUI Blazor App it's throwing the following exception:

[DOTNET] System.Net.Http.HttpRequestException: Connection refused (localhost:44363)
[DOTNET]  ---> System.Net.Sockets.SocketException (111): Connection refused
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken 
[DOTNET] cancellationToken) in System.Net.Sockets.dll:token 0x60002b9+0x1f
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) in System.Net.Sockets.dll:token 0x60002b3+0x26
[DOTNET]    at Syst
[DOTNET] em.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) in System.Net.Sockets.dll:token 0x600029c+0xa4
[DOTNET]    at System.Net.Http.HttpConnectionP
[DOTNET] ool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x1b4
[DOTNET]    --- End of inner exception stack trace ---
[DOTNET]    at System.Net.Http.HttpC
[DOTNET] onnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x298
[DOTNET]    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpReque
[DOTNET] stMessage request, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006ff+0x14d
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellati
[DOTNET] onToken) in System.Net.Http.dll:token 0x6000701+0x8f
[DOTNET]    at System.Net.Http.HttpConnectionPool.SendUsingHttp11Async(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f1
[DOTNET] +0x15d
[DOTNET]    at System.Net.Http.HttpConnectionPool.DetermineVersionAndSendAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f2+0x224
[DOTNET]    at System.Net.Http.HttpConne
[DOTNET] ctionPool.SendAndProcessAltSvcAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f3+0x8e
[DOTNET] ssage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f4+0x99
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequ07-16 12:39:49.024 I/DOTNET  (16085):    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationT
[DOTNET] oken) in System.Net.Http.dll:token 0x6000787+0x94
[DOTNET]    at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c3+0xd7
[DOTNET]    at MauiB
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\AuthorizationDelegatingHandler.cs:line 43
[DOTNET]    at Microsoft.E
[DOTNET] xtensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c8+0xef
[DOTNET] ge request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) in System.Net.Http.dll:token 0x600022f+0x1b7
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\Authoriz07-16 12:39:49.025 I/DOTNET  (16085):    at MauiBlazor.Share
[DOTNET] d.Services.UserService.LoginAsync(LoginModel loginModel) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Services\UserService.cs:line 132
[DOTNET]    at MauiBlazorApp.Components.IdentityComponents.LoginComponent.HandleValidSubmitAsync() in D:\GitHub
[DOTNET] \CleanArchitecture\src\ClientApps\MauiBlazorApp\MauiBlazorApp\Components\IdentityComponents\LoginComponent.razor.cs:line 51

There is no CORS issue because the API app is allowing any origin as follows:

services.AddCors(options =>
{
    options.AddPolicy(myAllowSpecificOrigins, builder =>
    {
        builder
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowed(_ => true);
    });
});

Please help me how can I overcome the issue so that I can make requests to the ASP.NET Core REST endpoints from .NET MAUI Blazor app successfully. Thanks in advance.

Author: TanvirArjel
Assignees: -
Labels:

area-System.Net.Http, untriaged

Milestone: -

@Eilon Eilon changed the title [Bug] Connection refused when calling ASP.NET Core API endpoints from .NET MAUI Blazor App [Bug] Connection refused when calling ASP.NET Core API endpoints from .NET MAUI App Dec 17, 2021
@Eilon Eilon changed the title [Bug] Connection refused when calling ASP.NET Core API endpoints from .NET MAUI App [Bug] SSL Connection refused when calling ASP.NET Core API endpoints from .NET MAUI App on Android Dec 17, 2021
@Eilon
Copy link
Member

Eilon commented Dec 17, 2021

Moved to dotnet/runtime.

TL;DR: The documented method for calling untrusted local SSL endpoint from the Android emulator does not appear to work in .NET MAUI apps in the Android emulator. It works from a Xamarin Android app. It also works from Windows.

@ghost
Copy link

ghost commented Jan 13, 2022

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

Issue Details

The ASP.NET Core Web API app is up and running. Making requests to the API endpoints from Blazor Web Assembly works perfectly fine. Whenever making request from the .NET MAUI Blazor App it's throwing the following exception:

[DOTNET] System.Net.Http.HttpRequestException: Connection refused (localhost:44363)
[DOTNET]  ---> System.Net.Sockets.SocketException (111): Connection refused
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken 
[DOTNET] cancellationToken) in System.Net.Sockets.dll:token 0x60002b9+0x1f
[DOTNET]    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) in System.Net.Sockets.dll:token 0x60002b3+0x26
[DOTNET]    at Syst
[DOTNET] em.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) in System.Net.Sockets.dll:token 0x600029c+0xa4
[DOTNET]    at System.Net.Http.HttpConnectionP
[DOTNET] ool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x1b4
[DOTNET]    --- End of inner exception stack trace ---
[DOTNET]    at System.Net.Http.HttpC
[DOTNET] onnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x6000700+0x298
[DOTNET]    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpReque
[DOTNET] stMessage request, Boolean async, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006ff+0x14d
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellati
[DOTNET] onToken) in System.Net.Http.dll:token 0x6000701+0x8f
[DOTNET]    at System.Net.Http.HttpConnectionPool.SendUsingHttp11Async(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f1
[DOTNET] +0x15d
[DOTNET]    at System.Net.Http.HttpConnectionPool.DetermineVersionAndSendAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f2+0x224
[DOTNET]    at System.Net.Http.HttpConne
[DOTNET] ctionPool.SendAndProcessAltSvcAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f3+0x8e
[DOTNET] ssage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) in System.Net.Http.dll:token 0x60006f4+0x99
[DOTNET]    at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequ07-16 12:39:49.024 I/DOTNET  (16085):    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationT
[DOTNET] oken) in System.Net.Http.dll:token 0x6000787+0x94
[DOTNET]    at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c3+0xd7
[DOTNET]    at MauiB
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\AuthorizationDelegatingHandler.cs:line 43
[DOTNET]    at Microsoft.E
[DOTNET] xtensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in Microsoft.Extensions.Http.dll:token 0x60000c8+0xef
[DOTNET] ge request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) in System.Net.Http.dll:token 0x600022f+0x1b7
[DOTNET] lazor.Shared.Common.AuthorizationDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Common\Authoriz07-16 12:39:49.025 I/DOTNET  (16085):    at MauiBlazor.Share
[DOTNET] d.Services.UserService.LoginAsync(LoginModel loginModel) in D:\GitHub\CleanArchitecture\src\ClientApps\MauiBlazor.Shared\Services\UserService.cs:line 132
[DOTNET]    at MauiBlazorApp.Components.IdentityComponents.LoginComponent.HandleValidSubmitAsync() in D:\GitHub
[DOTNET] \CleanArchitecture\src\ClientApps\MauiBlazorApp\MauiBlazorApp\Components\IdentityComponents\LoginComponent.razor.cs:line 51

There is no CORS issue because the API app is allowing any origin as follows:

services.AddCors(options =>
{
    options.AddPolicy(myAllowSpecificOrigins, builder =>
    {
        builder
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowed(_ => true);
    });
});

Please help me how can I overcome the issue so that I can make requests to the ASP.NET Core REST endpoints from .NET MAUI Blazor app successfully. Thanks in advance.

Author: TanvirArjel
Assignees: -
Labels:

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

Milestone: -

@steveisok
Copy link
Member

steveisok commented Feb 20, 2022

Once it goes in, this should be fixed by dotnet/android#6665

jonathanpeppers pushed a commit to dotnet/android that referenced this issue Apr 29, 2022
…HTTP handlers (#6665)

Context: dotnet/runtime#62966

The `AndroidClientHandler` and `AndroidMessageHandler` classes both
have the `ServerCertificateCustomValidationCallback` property, which
should be useful e.g. to allow running the Android app against a
server with a self-signed SSL certificate during development, but the
callback is never used. Unfortunatelly since .NET 6 the
`System.Net.Http.SocketsHttpHandler` for Android doesn't support the
use case anymore. That means that [the recommended way of connecting
to local web server][0] won't work in MAUI.

This PR introduces an implementation of `IX509TrustManger` which wraps
the default Java X509 trust manager and calls the user's callback on
top of the default validation.

It turns out that `X509Chain` `Build` function doesn't work on
Android, so I'm not calling it and I'm passing the chain to the
callback directly.

Additionally, we need a default proguard rule due to:

https://github.com/xamarin/xamarin-android/blob/46002b49d8c0b7b1a17532a8e104b4d31afee7a6/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/GenerateProguardConfiguration.cs#L50-L57

    -keep class xamarin.android.net.X509TrustManagerWithValidationCallback { *; <init>(...); }

`Mono.Android.dll` is skipped during the
`GenerateProguardConfiguration` linker step. It might be worth
addressing this in a future PR.

[0]: https://docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/connect-to-local-web-services
jonathanpeppers pushed a commit to jonathanpeppers/xamarin-android that referenced this issue Apr 29, 2022
…HTTP handlers (dotnet#6665)

Backport of: dotnet#6665
Context: dotnet/runtime#62966

The `AndroidClientHandler` and `AndroidMessageHandler` classes both
have the `ServerCertificateCustomValidationCallback` property, which
should be useful e.g. to allow running the Android app against a
server with a self-signed SSL certificate during development, but the
callback is never used. Unfortunatelly since .NET 6 the
`System.Net.Http.SocketsHttpHandler` for Android doesn't support the
use case anymore. That means that [the recommended way of connecting
to local web server][0] won't work in MAUI.

This PR introduces an implementation of `IX509TrustManger` which wraps
the default Java X509 trust manager and calls the user's callback on
top of the default validation.

It turns out that `X509Chain` `Build` function doesn't work on
Android, so I'm not calling it and I'm passing the chain to the
callback directly.

Additionally, we need a default proguard rule due to:

https://github.com/xamarin/xamarin-android/blob/46002b49d8c0b7b1a17532a8e104b4d31afee7a6/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/GenerateProguardConfiguration.cs#L50-L57

    -keep class xamarin.android.net.X509TrustManagerWithValidationCallback { *; <init>(...); }

`Mono.Android.dll` is skipped during the
`GenerateProguardConfiguration` linker step. It might be worth
addressing this in a future PR.

[0]: https://docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/connect-to-local-web-services
jonpryor pushed a commit to dotnet/android that referenced this issue May 9, 2022
…HTTP handlers (#6972)

* [Mono.Android] custom validation callback for server certificates in HTTP handlers (#6665)

Backport of: #6665
Context: dotnet/runtime#62966

The `AndroidClientHandler` and `AndroidMessageHandler` classes both
have the `ServerCertificateCustomValidationCallback` property, which
should be useful e.g. to allow running the Android app against a
server with a self-signed SSL certificate during development, but the
callback is never used. Unfortunatelly since .NET 6 the
`System.Net.Http.SocketsHttpHandler` for Android doesn't support the
use case anymore. That means that [the recommended way of connecting
to local web server][0] won't work in MAUI.

This PR introduces an implementation of `IX509TrustManger` which wraps
the default Java X509 trust manager and calls the user's callback on
top of the default validation.

It turns out that `X509Chain` `Build` function doesn't work on
Android, so I'm not calling it and I'm passing the chain to the
callback directly.

Additionally, we need a default proguard rule due to:

https://github.com/xamarin/xamarin-android/blob/46002b49d8c0b7b1a17532a8e104b4d31afee7a6/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/GenerateProguardConfiguration.cs#L50-L57

    -keep class xamarin.android.net.X509TrustManagerWithValidationCallback { *; <init>(...); }

`Mono.Android.dll` is skipped during the
`GenerateProguardConfiguration` linker step. It might be worth
addressing this in a future PR.

[0]: https://docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/connect-to-local-web-services

* Update .apkdesc (probably merge issue)

Co-authored-by: Simon Rozsival <simon@rozsival.com>
@simonrozsival
Copy link
Member

A fix for this issue has been shipped with MAUI GA.

@Eagle3386
Copy link

Eagle3386 commented Jun 15, 2022

@simonrozsival Just to clarify things for MAUI (Blazor) consumers like me:

Doing

ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) =>
  "CN=localhost".Equals(certificate?.Issuer, StringComparison.OrdinalIgnoreCase)
  || errors == System.Net.Security.SslPolicyErrors.None

inside my MauiProgram.cs is supposed to result in a NRE due to the native Android handler getting called (stack trace excerpt):

in System.Net.Http.HttpClientHandler.InvokeNativeHandlerMethod
in System.Net.Http.HttpClientHandler.SetServerCertificateCustomValidationCallback
in System.Net.Http.HttpClientHandler.set_ServerCertificateCustomValidationCallback

as is pointed out over at Bypass the certificate security check where it states:

// This method must be in a class in a platform project, even if
// the HttpClient object is constructed in a shared project.

, right?

If so, why do the aforementioned docs then state I should do new HttpClientHandler(); in that platform-specific class, i. e. Platforms\Android\MainApplication.cs, when in reality I explicit must instantiate new AndroidMessageHandler(); in that very class? Because I tried & only prevented the NRE with the latter handler…

Moreover, since you stated in #68898 that

[...] the native handlers will use the ServerCertificateCustomValidationCallback property in .NET 7 (see dotnet/android#6665, mac/iOS is implementation is currently in progress) [...]

I'm totally confused how to set/overwrite the callback for debug purposes according to "current best practices".

Up to your comment, I was using this SO answer's solution to circumvent the Android Emulator failing the certificate validation.

Therefore, any help, i. e. clarification on proper ServerCertificateCustomValidationCallback usage for MAUI, would be greatly appreciated! 😉

@simonrozsival
Copy link
Member

@Eagle3386 hi! You shouldn't get any NRE exception. The HttpClientHandler class is a proxy for AndroidMessageHandler on Android so there shouldn't really be any difference.

I tried reproducing it locally in a new MAUI Blazor project and I didn't get any NRE. Can you send me a minimum reproducible example based on a new MAUI Blazor app project?

danch15 added a commit to danch15/MauiAndDevDemo that referenced this issue Jun 16, 2022
@danch15
Copy link

danch15 commented Jun 16, 2022

@simonrozsival I use Microsoft.AspNetCore.SignalR.Client.HubConnection and the DangerousTrustProvider and DangerousAndroidMessageHandlerEmitter Class, which can be started normally on Android 11, but the same code cannot be started on Android 7.1.2. (Windows Machine is OK too.)
See: https://stackoverflow.com/questions/71047509/trust-anchor-for-certification-path-not-found-in-a-net-maui-project-trying-t

private HubConnection Connect(string urls, CookieCollection authCookie)
{
    return new HubConnectionBuilder().WithUrl(urls,
        options =>
        {
            options.Cookies.Add(authCookie);
            options.WebSocketConfiguration = conf =>
            {
                conf.RemoteCertificateValidationCallback = (message, cert, chain, errors) => { return true; };
            };
        })
        .WithAutomaticReconnect()
        .Build();
}
<PropertyGroup>
	<AndroidHttpClientHandlerType Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">DangerousAndroidMessageHandler, DangerousAndroidMessageHandler</AndroidHttpClientHandlerType>
</PropertyGroup>

The ServerCertificateCustomValidationCallback seems to have no practical effect on Android System.

By the way, the httpClient.PostAsync can be started on Android 7.1.2 and 11 if I use the DangerousTrustProvider and DangerousAndroidMessageHandlerEmitter Class. But the HubConnection cannot be started in Android 71.2, I got the Exception:

{System.Net.Http.HttpRequestException: Response status code does not indicate success: 400 (Bad Request).
   at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
   at Microsoft.AspNetCore.Http.Connections.Client.Internal.SendUtils.SendMessages(Uri sendUrl, IDuplexPipe application, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()
   at System.IO.Pipelines.Pipe.DefaultPipeReader.GetResult(Int16 token)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.HandshakeAsync(ConnectionState startingConnectionState, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken)
   at System.Threading.Tasks.ForceAsyncAwaiter.GetResult()
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)
   at MauiHubConnDemo.HubHelper.ConnectWithRetryAsync(HubConnection connection) in D:\Project\notshell\HubHelper.cs:line 57}

My Example code see here: https://github.com/danch15/MauiAndDevDemo/blob/main/MauiHubConnDemo.rar

================================================================================
It seems like the problem is "options.Cookies.Add(authCookie);", I add a webserver with signalR, then connect to it without cookie, it OK.

@Eagle3386
Copy link

Eagle3386 commented Jun 16, 2022

@Eagle3386 hi! You shouldn't get any NRE exception. The HttpClientHandler class is a proxy for AndroidMessageHandler on Android so there shouldn't really be any difference.

@simonrozsival Alright, that's how I thought it ought to work. Thanks for the clarification! 👍🏻

I tried reproducing it locally in a new MAUI Blazor project and I didn't get any NRE. Can you send me a minimum reproducible example based on a new MAUI Blazor app project?

Of course, but I'm having a really hard time producing such a POC: instead of the NRE, I'm constantly getting Java.Security.Cert.CertificateException: 'The remote certificate was rejected by the provided RemoteCertificateValidationCallback.' with the POC's code running inside the Android Emulator.
Though, researching that exception leads to numerous sources all telling me it's Xamarin's fault due to including the root cert while it's specified that it should be excluded, etc.

Can you help me yet again?

@Eilon
Copy link
Member

Eilon commented Jun 16, 2022

Hi everyone, I created a little helper class that I started using that works on Android and Windows to connect to "local" SSL: https://gist.github.com/Eilon/49e3c5216abfa3eba81e453d45cba2d4

And here's how you can use it to call from an Android emulator to a Windows-hosted ASP.NET Core app:

        var devSslHelper = new DevHttpsConnectionHelper(sslPort: 7155);
        var http = devSslHelper.HttpClient;
        var responseText = await http.GetStringAsync(devSslHelper.DevServerRootUrl + "/someApi");

And to use SignalR client in .NET MAUI, here's how I use the helper:

        var devSslHelper = new DevHttpsConnectionHelper(sslPort: 7155);

        var hubConnection = new HubConnectionBuilder()
#if ANDROID
            .WithUrl(devSslHelper.DevServerRootUrl + "/myhub"
                , configureHttpConnection: o =>
                {
                    o.HttpMessageHandlerFactory = m => devSslHelper.GetPlatformMessageHandler();
                }
                )
#else
            .WithUrl(devSslHelper.DevServerRootUrl + "/myhub")
#endif
            .Build();

        hubConnection.On<SomeThing>("SomeApi", ...);

        await hubConnection.StartAsync();

It seems that to make it work on Android, there are two places in the handler that need to make the SSL checks go through:

  1. You need a ServerCertificateCustomValidationCallback that bypasses cert checks for localhost
  2. You need an IHostnameVerifier that reports that the 10.0.2.2 host is valid (that's how Android talks to the Windows host)

@Eagle3386
Copy link

Eagle3386 commented Jun 16, 2022

@Eilon while that's an awesome helper for local debugging purposes, my scenario differs in that my service, i. e. HttpClient's target, is
a) already hosted on a publicly available server (think of sub.example.net)
b) has a valid wildcard certificate (i. e. *.example.net, for which my company even paid instead of using a Let's Encrypt one & that has the intermediate CA "Sectigo RSA Domain Validation Secure Server CA" & the root CA "USERTrust RSA Certification Authority")
c) my set ServerCertificateCustomValidationCallback enters with errors already being set to RemoteCertificateChainErrors

And according to

https://github.com/xamarin/xamarin-android/blob/3d5231ca3a094b85302b5dbd3cc074be37efa2c0/src/Mono.Android/Xamarin.Android.Net/X509TrustManagerWithValidationCallback.cs#L56-L60

(Inline code preview seems to be prevented for cross-linked repos…)

that error is only set if the internal TrustManager threw an exception - which I neither see at VS's debug outputs nor within the Android Emulator, hence I simply don't know what actually failed within the remote certificate chain. 😢

@Eilon
Copy link
Member

Eilon commented Jun 16, 2022

@Eagle3386 ah alright that might very well be different. My helper is only for locally hosted sites (e.g. ASP.NET Core running on your Windows machine).

@Eilon
Copy link
Member

Eilon commented Jun 16, 2022

I started a discussion topic on how to connect from Android emulators to a local ASP.NET Web API running on Windows: dotnet/maui#8131

Please check that out and let us know if you have any feedback on any of the solutions presented.

@Eagle3386
Copy link

Eagle3386 commented Jun 17, 2022

@Eilon & @simonrozsival I still implemented Eilon's snippet nonetheless, but its CustomHostnameVerifier isn't even reached: the CustomAndroidMessageHandler's assigned ServerCertificateCustomValidationCallback value is accessed first & since I'm in scenario 4 mentioned over at dotnet/maui#8131 (remote test server & real SSL certificate) the shouldn't ever happen, right?

Also, I did notice these debug output lines in VS (Callback.NRE is my current attempt for Simon's initial POC request):

Resolved pending breakpoint at 'HttpsHelper.cs:38,1' to bool Callback.NRE.HttpsHelper.<>c.<GetPlatformMessageHandler>b__12_0 (System.Net.Http.HttpRequestMessage message, System.Security.Cryptography.X509Certificates.X509Certificate2 cert, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors errors) [0x00000].
[EngineFactory] Provider GmsCore_OpenSSL not available

Is my RemoteCertificateChainErrors caused due to missing OpenSSL?
Though, I'm running the Android Emulator's default image for Android 11...

Furthermore, ChainStatus is empty, so I'm still unable to determine the real cause:
image

Lastly, please let me know if I should post this over at the mentioned discussion instead of this issue. I'd almost do anything just to get this chain error resolved… 😅

@Eagle3386
Copy link

Just for the sake of completeness: using @Eilon's helper helped (pun intended! 😎) solve the NRE issue & also enabled me to discover that ASP.NET Core's dev cert was included in the chain - although "invisible" as anyone can see in my screenshot above (showing 3 instead of 4 certs).

Everything else was/is discussed over at dotnet/maui/discussions/8131.

@ghost ghost locked as resolved and limited conversation to collaborators Aug 18, 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

9 participants