Closed ComptonAlvaro closed 2 years ago
Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.
Author: | ComptonAlvaro |
---|---|
Assignees: | - |
Labels: | `area-System.Net.Http` |
Milestone: | - |
Tagging subscribers to 'arch-android': @steveisok, @akoeplinger See info in area-owners.md if you want to be subscribed.
Author: | ComptonAlvaro |
---|---|
Assignees: | - |
Labels: | `area-System.Net.Http`, `os-android`, `untriaged` |
Milestone: | - |
Tagging subscribers to this area: @dotnet/ncl, @vcsjones See info in area-owners.md if you want to be subscribed.
Author: | ComptonAlvaro |
---|---|
Assignees: | - |
Labels: | `area-System.Net.Http`, `area-System.Net.Security`, `os-android`, `untriaged` |
Milestone: | - |
cc: @simonrozsival
@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.
@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.
@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.
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.
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.
@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.
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:
SocketsHttpHandler
directlyHttpClientHandler
+ configuration to ensure it creates SocketsHttpHandler
internally (I don't know the config name off the top of my head).When Android is supported, then I agree that gRPC+Android docs are required. There might also be the opportunity to detect at runtime that the current platform is Android and check that a valid handler is configured.
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,
};
@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.
@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>
.
@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
@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
...
@simonrozsival In summary, now I have this:
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>
@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>
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.
@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.
@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.
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:
The code of the library is this:
The WPF client:
The MAUI client:
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.