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

Mutual Authentication: Certificate chain removed when connecting to server in MAUI app but not Xamarin Forms #100602

Closed
vsfeedback opened this issue Apr 2, 2024 · 70 comments
Assignees
Labels
area-System.Net.Security needs-author-action An issue or pull request that requires more info or actions from the author. os-android
Milestone

Comments

@vsfeedback
Copy link

vsfeedback commented Apr 2, 2024

This issue has been moved from a ticket on Developer Community.


Hi,

When connecting to the server but running the code using Xamarin Forms, the server received the full certificate chain.
Using MAUI .Net 7 or 8.0, only the client certificate is sent to the server. Certificate chain is empty.
Same certificate and server are used.

In HttpWebRequest ServerCertificateValidationCallback, chain.ChainElements is empty.
Certificate received by the server is valid.

Does anyone knows how to fix this?

Here's a snippet on the client:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);

req.ServerCertificateValidationCallback = ValidateServerCertificate;
req.ProtocolVersion = HttpVersion.Version11;
req.ClientCertificates = certificates;
req.Method = "GET";
req.Accept = "text/plain";
req.KeepAlive = false;
...

private static bool ValidateServerCertificate(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
{

}

Original Comments

Feedback Bot on 2/12/2024, 05:46 PM:

(private comment, text removed)


Original Solutions

(no solutions)

@mattleibow mattleibow transferred this issue from dotnet/maui Apr 3, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Apr 3, 2024
@ManickaP
Copy link
Member

ManickaP commented Apr 8, 2024

I'm a bit confused, where is the chains missing? ServerCertificateValidationCallback is for server certificate. ClientCertificates is for sending client certificate to the server. So on which side is the problem?

Also if you're on .NET 7/8, why are you using HttpWebRequest instead of HttpClient? We do not recommend HttpWebRequest for any new development, it's there only for backward compat and easier transition from .NET Framework. It has limited set of features and doesn't get any new development.

@ManickaP ManickaP added the needs-author-action An issue or pull request that requires more info or actions from the author. label Apr 8, 2024
@NexusMobile
Copy link

The missing chains is at the server. Server receives only chain[0]. All the other certificates in the chain are not provided.
Using httpClient provides the same result at the server. Only the client cert is sent.
In the ValidateServerCertificate callback function, chain.ChainElement.Count = 0 that confirms chain is not sent.
This happen using Net6 and Net8 apps.

Using Xamarin Forms app with httpClient or HttpWebRequest both send the full chain of certificate to the server

@ManickaP
Copy link
Member

ManickaP commented Apr 9, 2024

Can you provide us with a repro code? Preferably for both sides? Also if you have a working code, could you share that as well?

Using httpClient provides the same result at the server. Only the client cert is sent.

You can use HttpClient in a server code, but it is still in the role of a client in some other communication with another server. So some of the description still confuses me.

@NexusMobile
Copy link

NexusMobile commented Apr 9, 2024

Hi,

Client code snippet:

/* ************ */
.
.
.

var httpClientHandler = new HttpClientHandler();

httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
httpClientHandler.SslProtocols = SslProtocols.Tls12;
httpClientHandler.ServerCertificateCustomValidationCallback = ValidateServerCertificate;

/* clientCert contains client CA certificates
httpClientHandler.ClientCertificates.Add(clientCert);

HttpClient httpClient = new HttpClient(httpClientHandler);

var username = "test@xxxx.com";
var password = "123456";

string encoded = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + encoded);
httpClient.DefaultRequestHeaders.Add("Accept", "text/plain");

httpClient.BaseAddress = new Uri("https://192.168.0.109:8443");

HttpResponseMessage response = await httpClient.GetAsync(****URL****);
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
.
.
.

private static bool ValidateServerCertificate(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors  slPolicyErrors)
{
               if (chain != null)
                   Console.WriteLine("chain: {0}", chain.ChainElements.Count); 
               // Printed value is: [DOTNET] chain: 0
               // Same code running in Xamarin Froms, printed value is: chain: 4
               return true;
}

/* ************ */

At the server, we get the same result in ssl handshake function as above regarding certificate chain received from client:
Chain count of 0 and chain count of 4.

BTW, this is a mutual authentication SSL handshake using private CA

Does it help understand the issue?

Regards

@ManickaP
Copy link
Member

ManickaP commented Apr 9, 2024

So the problem is in provided server certificate chain do the validation callback and that it differs based on what platform you compile for? And you claim this difference is between Xamarin Forms and MAUI? Isn't Xamarin foundation of MAUI?

@simonrozsival do you know of any difference there?

@NexusMobile
Copy link

The problem is to provide certificate chain from the client to the server.
From the sample provided above the client does not send the certificate chain to the server.
It works using Xamarin Forms but not using MAUI net 8.0 on Android
I don't understand your last question: Isn't Xamarin foundation of MAUI?
How can I tell?
We are using VS 2022 17.9.5 testing on both platform.

@simonrozsival
Copy link
Member

@NexusMobile can you please try setting <UseNativeHttpHandler>false</UseNativeHttpHandler> in your csproj file and try again? I believe the support for client certificates in Android's native message handler is not working as expected (dotnet/android#7274).

@NexusMobile
Copy link

false already in csproj file to fix httpClientHandler.ClientCertificates being null without it.

@NexusMobile
Copy link

While building project using Xamarin Forms it works fine without <UseNativeHttpHanlder>false<\UseNativeHttpHanlder>
Building a MAUI project with .Net8 using the setting above solve only httpClientHanlder.ClientCertificates not being null and have the client send his certificate (without the chain) to the server.

@simonrozsival
Copy link
Member

I see. I originally misread the issue and I thought the problem is with sending the client certificate to the server. The problem actually is that the server's certificate chain doesn't seem to be passed to the ServerCertificateValidationCallback on the client correctly.

Isn't Xamarin foundation of MAUI?

Xamarin.Forms and MAUI have different implementation of the networking layer. Xamarin.Forms uses BoringSSL or OpenSSL and MAUI/.NET 8 on Android uses Android's platform APIs.

@simonrozsival simonrozsival self-assigned this Apr 10, 2024
@simonrozsival simonrozsival added this to the 9.0.0 milestone Apr 10, 2024
@NexusMobile
Copy link

Unless mistaken this is my understanding too.
What would be the next step to get t his resolved?
Thanks again

@simonrozsival
Copy link
Member

simonrozsival commented Apr 10, 2024

@NexusMobile is the server's certificate self-signed? if it is, this would be a duplicate of #84202

The relevant part from the previous discussion:

My understanding of Android is that ChainElements on X509Chain are not populated unless the chain is valid. Contrast this with other operating systems where the chain elements is usually populated, even for partial or incomplete chains.

@NexusMobile
Copy link

@simonrozsival, Client and server certificates are self-signed by the same CA. For us the certification is valid.
When using Xamarin Forms, we validate the client certificate in code at the server.
I've read issue #84202 and I don't think this is the same issue.
Unless mistaken the server certificates is not involved yet at this point, only the client certificate is involved.

@ManickaP ManickaP added os-android and removed untriaged New issue has not been triaged by the area owner labels Apr 11, 2024
@NexusMobile
Copy link

Hi,
Do you need anything else from us?
This issue has tag needs-author-action
Regards

@simonrozsival
Copy link
Member

Unless mistaken the server certificates is not involved yet at this point, only the client certificate is involved.

I think there is still some confusion in this thread. In the client code in the ValidateServerCertificate, the second parameter certificate is the server's certificate sent to the client and the third parameter chain is the server's certificate chain, not the client's.

If I understand your situation correctly, you should include your CA's public key (and possibly also the public keys of the intermediate certifcates) in the client app using network_security_config.xml. Does the network config solve the issue for you?

@NexusMobile
Copy link

My mistake. You are absolutely right.

I've tried network_security_config.xml and I still get an empty chain.
I've confirm also using Wireshark the server's certificate and chain were sent to the app.

Thanks

@NexusMobile
Copy link

I've replaced the self-signed Certificate the server sends to the client with a public certificate and I get the same empty chain.
What would be the next step?

Is it possible to have access to the source code?

@simonrozsival
Copy link
Member

I've replaced the self-signed Certificate the server sends to the client with a public certificate and I get the same empty chain.

That seems like bug and it is different from the issue I shared previously. I need to be able to replicate the issue so I can diagnose it. Can you create a minimal repro in a new MAUI project and share that as a public github repo? Ideally make requests to a public website, such as https://microsoft.com or https://badssl.com (there are multiple subdomains for example with a self-signed certificate).

@NexusMobile
Copy link

I will look into this.
Where can I find "(there are multiple subdomains for example with a self-signed certificate)?
I have found only this one: https://self-signed.badssl.com/
And connecting the app to this site return the same empty chain.
Are there others I can try?

Also from our original post, the client's certificate sent to the server has his chain removed. The certificate's chain count should be 4 in our case but it returns 0.

Thanks

@simonrozsival
Copy link
Member

I will look into this.

Thanks!

I have found only this one: https://self-signed.badssl.com/

I think that is the most relevant one for your use case. The https://client.badssl.com/ should also be relevant for the other half of the problem you describe.

@Coccoliso-1963
Copy link

The CACertificate problem is extensive and has appeared since 17.8 and it concerns the certificates.
From version VS2022-17.8 an Android 13 application in net maui 7.0 no longer connects to a wcf application with certificate if compiled in release ( real device or emulator) but it works if in debug. At the time I rolled it back VS2022 to make it work. Now with vs 2022 17.9.6 in debug the exception throwed is is "not connected" and in release the app is killed at startup (real device or emulator ). The matter has not been resolved even in net maui 8.0, it's the same, no connection in debug or release in Android 13 or 14. The application was created as soon as net maui 7.0 was made available and it never gave any problems even with the auto update.. and it still works if I don't recompile it with VS2022 versions after 17.7.7. Several users use it in production..I feel slightly cheated.

@Coccoliso-1963
Copy link

` var bind = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
bind.MaxReceivedMessageSize = 2147483647;
bind.MaxBufferSize = 2147483647;
bind.ReaderQuotas.MaxArrayLength = 2147483647;
bind.SendTimeout = new TimeSpan(0, 5, 0);

            FEWCF.FEWCFClient _WS = new FEWCF.FEWCFClient(bind, new EndpointAddress(url));
            try
            {
                _WS.ClientCredentials.ClientCertificate.SetCertificate(_storelocation,
                                                                       _storename,
                                                                       X509FindType.FindByThumbprint,
                                                                       _Thumbprint);
            }
            catch
            {
            }

`
.. with StoreLocation System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser and StoreName System.Security.Cryptography.X509Certificates.StoreName.Root
All works until VS2022 17.7.7 then "null".

@NexusMobile
Copy link

Hi,

Any progress/update about this issue?

Thanks

@simonrozsival
Copy link
Member

For purpose of just creating the chain I feel it would be OK to claim all the extra certificates as trusted e.g. add them to both ExtraStore and CustomTrustStore if that makes it work on Android.

I thought this is where the SslCertificateTrust? trust parameter would be used, but it's not. What is the purpose of that parameter?

and do you know if Android really does exclude systems CAs in custom mode

yes, it excludes the system CAs

@wfurt
Copy link
Member

wfurt commented Jun 12, 2024

The "Trust" was primarily added to support #45456. It is also used later for validation in SslStream but it is not currently used for constructing the chain itself e.g. the logic assumes that any additional "useful" certificates will be in the "intermediates" collection (as it pre-dates the Trust object)

I would not object to use the trust in chain construction in way similar to

if (trust != null)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
if (trust._store != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
}
if (trust._trustList != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
}
}

So far that was not needed by anybody. That would leave Android as special case but at least there would be path forward for users to make it work. It would make the use more complicated e.g. if you have certificate that is already trusted by the platform you should leave it empty and if you do have something that is not trusted you would need to construct the extra object.

We could take extra steps to try to hide some of the platform differences to make it more consistent and easier for Android users. I can take first stab at it @simonrozsival but I don't have any good way at the moment how to test on Android.

Copy link
Contributor

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

Copy link
Contributor

This issue has been automatically marked no-recent-activity because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.

@NexusMobile
Copy link

Are you close to fix this issue?
Thanks

@simonrozsival
Copy link
Member

@NexusMobile yes, with the #103372 fix for SslStreamCertificateContext.Create, this is now fixed for .NET 9.
@wfurt is this something we would want to backport to a .NET 8 servicing release or is that too risky/not justified enough?

@wfurt
Copy link
Member

wfurt commented Jun 28, 2024

I don't think it is too risky. But we would need strong justification for servicing.

@NexusMobile
Copy link

Unless mistaken, all Visual studio 2022 Android developer will not be able to use .Net 8 Mutual Authentication if it is not retrofited.

This is why we raised this issue.

@NexusMobile
Copy link

Has a decision been made to backport to .Net 8?

@simonrozsival
Copy link
Member

I opened a backport but it's not yet clear if it will be approved or not: #104541

@NexusMobile
Copy link

Thanks.
If I use your changes in my app of the code you've added to .Net 9, do you think it will work in .Net 8?
Could you share your changes?
Thanks again

vitek-karas added a commit that referenced this issue Jul 15, 2024
…CertificateContext (#104541)

Backport of #103372 and #104016 to release/8.0-staging

## Customer Impact

- [X] Customer reported (#100602)
- [ ] Found internally

Customers developing Android apps are currently unable to use mutual TLS authentication in certain cases as the `SslStreamCertificateContext.Create(...)` method will fail to build an X509Chain instance if the certificate isn't trusted by the OS due to the limitations of the Android platform.

## Regression

- [ ] Yes
- [X] No

## Testing

Unit tests and manual testing on Android emulator.

## Risk

Low. The change is mostly limited to Android where this API doesn't currently work in many cases. 

---------

Co-authored-by: Tomas Weinfurt <tweinfurt@yahoo.com>
Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com>
@vitek-karas
Copy link
Member

Backported to 8.0 via #104541. Closing as fixed.

@NexusMobile
Copy link

When can we expect a new release with the bug fixes available?

@vitek-karas
Copy link
Member

@NexusMobile should be out mid-August... if everything works as planned.

@NexusMobile
Copy link

Thanks
In the mean time can we have access to fixes source code?
I would like to test changes in our App.

@vitek-karas
Copy link
Member

The source code for the fixes is all open source: https://github.com/dotnet/runtime/pull/104541/files
But I don't know how workable it is to adapt it without the fix in the framework itself. You could build your own build of the framework, but then it's relatively tricky to publish an app with it and it's definitely not a supported scenario in any way or form.

@wfurt
Copy link
Member

wfurt commented Jul 16, 2024

since it is merged you can simply build the staging branch @NexusMobile. And there will be binary bits published as well if you want them. Since the cutoff for merges was today I expect new 8.0 daily build in next few days.

@NexusMobile
Copy link

Thanks All, :-)
Using source code is for testing only, no worry. ;-)
Once 8.0 is build, will it be released automatically in Windows Update or do we need to download it separately and how?
Thanks.

@vitek-karas
Copy link
Member

MAUI apps package the runtime into the app - so you will have to rebuild your app with a new SDK. I don't think SDK updates via Windows Update, it may update via VS, but if you want to be sure, download it from https://dot.net .

@NexusMobile
Copy link

Super. Thanks again.

@github-actions github-actions bot locked and limited conversation to collaborators Aug 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security needs-author-action An issue or pull request that requires more info or actions from the author. os-android
Projects
None yet
Development

No branches or pull requests

8 participants