Bug 5546 - ServicePointManager.ServerCertificateValidationCallback does not validate certificate chain in MonoTouch
Summary: ServicePointManager.ServerCertificateValidationCallback does not validate cer...
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 5.2
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Sebastien Pouliot
Depends on:
Reported: 2012-06-06 05:32 UTC by René Ruppert
Modified: 2012-09-18 08:25 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

System.dll hotfix (1.04 MB, application/octet-stream)
2012-06-06 22:19 UTC, Sebastien Pouliot

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description René Ruppert 2012-06-06 05:32:41 UTC
Found in MonoTouch 5.2.12.

This is related to: http://stackoverflow.com/questions/10895138/what-checks-has-mono-performed-when-system-net-servercertificatevalidationcallba

In MonoTouch, the certificate chain is not validated correctly. In Mono (on Mac) and in .NET (on Windows) an error is risen, where MonoTouch claims "everything okay".

Have a server that has an SSL certificate that is derived from a CA that is unknown/untrusted. In our case, this is ca.brainloop.com and is used to create internal test certificates. The certificate's name matches the one of the server.
I cannot give you access to our test setup because it is in our intranet but you'll be able to reproduce this with any other CA.

Steps to reproduce:
Use Safari on the iOS Simulator to navigate to "https://your.test.server". You will see a warning that the used certificate cannot be validated. This is expected behavior.
Then make a small test app that connects to the same server using WebClient for instance. Implement the certificate validation code as shown below:

ServicePointManager.ServerCertificateValidationCallback = delegate( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors )
				Console.WriteLine( "Sender {0}, Certificate {1}, Chain {2}, Error {3}", sender, cert, chain, errors );
				return true;

If you run the app, there will be no error and the "chain" will be NULL.

Run the same code in Mono for Mac (or Windows): the chain will contain two elements and you'll get an SSL error in "errors" that indicates a validation problem of the chain.

My conclusion:
If I look at https://github.com/mono/mono/blob/master/mcs/class/System/System.Net/ServicePointManager.cs#L455 I can see that for MonoTouch the chain is explicitly NOT getting checked. But it seems that iOS isn't doing it either.
Comment 1 René Ruppert 2012-06-06 06:20:08 UTC
Some more notes:

* According to PouPou, iOS should take care of the basic chain validation.But how would it if I'm using .NET Web Services? To my knowledge, there is no native code involved. If in case of MT the chain validation is skipped, the certificate will be accepted.

* Also from PouPou: the chain validation is supposed to be an expensive operation but is that a valid excuse for different behavior on MT compared to Mono/.NET?  

* If I want to add the chain validation myself, I'm facing a problem: how to get all certificates of the chain?  internal ValidationResult ValidateChain (Mono.Security.X509.X509CertificateCollection certs) already receives a collection of certificates. I only get access to the one that is passed in in the service callback. However, if I create a chain from that single certificate, it will already return the expected chain validation error.
Comment 2 Sebastien Pouliot 2012-06-06 10:49:17 UTC
> the chain is explicitly NOT getting checked

Wrong. The chain validation is delegated to iOS. Look at:

> To my knowledge, there is no native code involved.

Wrong, see link above. If you follow it you'll see where the native calls are made. .NET web services calls ServicePointManager which will call into (native) iOS.

> If in case of MT the chain validation is skipped,

I don't doubt there's a bug but the validation is not skipped (the code is there) and people reported issues, from time to time, for specific certificates (so it occurs, at least, under some conditions).

> the chain validation is supposed to be an expensive operation but is that a valid excuse for different behavior on MT compared to Mono/.NET?

That was not an excuse - but a fact ;-) Having the OS handle this means the state (and cost) is shared across all the applications. Mono itself can do this (a single runtime and many apps) but for MonoTouch every application has it's own runtime. Having every application handle this itself (and differently from iOS) is problematic, e.g. you want your application to access the same servers as Safari does. This is why the chain validation is delegated to iOS.

FWIW Mono and .NET behaviour are really different since they both depends on what's in their stores (which can differ quite a bit). 

Mono's default to empty stores unless/until mozroots is used to populate them.

.NET varies even more because it delegates the check to CryptoAPI which differs a bit in each version of Windows (it does better with newer versions than older ones). Windows Update will also affect what the store contains (but those are generally not updates that are enabled by default). Enterprises configuration
 (e.g. ActiveDirectory) can also add/remove CA/roots from stores.

> If I want to add the chain validation myself, I'm facing a problem: how to get all certificates of the chain? 

That's why I said it's an expensive operation ;-)

SSL servers do not have to give you the chain (or the root) so your application is responsible to create it's own chain. IOW find them, download them, maintain them and their CRL and finally use X509Chain to build it (.NET only provides the last step, Windows is responsible for all the previous ones, unless you want to provide your own).

You can find some scenarios in: http://www.mono-project.com/UsingTrustedRootsRespectfully
Comment 3 René Ruppert 2012-06-06 16:24:48 UTC
I get your points and I indeed missed the code that delegates the call to iOS. Looking at it, it would be very interesting if the issue is actually the "try-catch { // Ignore }".

Please, what I said about skipped code being an excuse: no offense! Nothing personal! Sorry if you interpreted it that way.

To get my specific scenario right, I wouldn't even need any online checks. The certs I check are either issued by Geotrust or not. All Geotrust certs should be available offline on the device. And Safari on the device behaves exactly as expeted: it complains about the test certs but not the ones issued by Geotrust.
If I use the ServicePointManager callback however, both cases result in "no error".

But I'm sure you'll find it out :-) I put all my trust in Xamarin's hands!

Meanwhile I'll use some code from the link you posted and check if that helps me to verify the certificate.
Comment 4 Sebastien Pouliot 2012-06-06 20:59:14 UTC
A SecTrustResult.RecoverableTrustFailure (5) is returned (by iOS) but it's never given to a policy or callback (the two ways to change the result). The default ICertificatePolicy, seeing nothing, accepted the certificate. In other cases (e.g. mismatching names) the errors are propagated correctly (and the certificate refused).

> Meanwhile I'll use some code from the link you posted and check if that helps me to verify the certificate.

Doubtful - at least not without a lot of work since it requires duplicating quite a bit of logic (like certificate retrieval) that are not done by the .NET framework, i.e. by the OS (e.g. Windows .NET) or additional tools (e.g. Mono).

OTOH I can attach an hotfix for 5.2.12 once my testing is over (and the backport is done).
Comment 5 Sebastien Pouliot 2012-06-06 22:15:48 UTC
Fixed in
master: 74d5d4fbe3598b9852af2846643991ea30020542
5.2-series: 8675ef47c7853dc458edab04ad32a27c027e21d7

QA: unit tests added in master
Comment 6 Sebastien Pouliot 2012-06-06 22:19:43 UTC
Created attachment 2026 [details]
System.dll hotfix

To use the attached assembly (on top of MonoTouch 5.2.12) do:

1) backup your /Developer/MonoTouch/usr/lib/mono/2.1/System.dll and /Developer/MonoTouch/usr/lib/mono/2.1/System.dll.mdb files

2) copy the attached file to /Developer/MonoTouch/usr/lib/mono/2.1/System.dll

3) remove the /Developer/MonoTouch/usr/lib/mono/2.1/System.dll.mdb symbols (they won't match anymore)

4) clean, rebuild and test your application

5) Let us know if this fixes your issue
Comment 7 René Ruppert 2012-06-11 04:16:06 UTC
Yes, I can confirm that the issue has been fixed. Is it safe to keep the hot fixed system.dll and submit apps with it? Are there any other changes in it? Is it possible to get matching debug symbols?
Comment 8 David 2012-09-18 04:53:40 UTC
MonoTouch v5.0.4 keeps returning the X509Chain as null in the ServicepointManager.ServerCertificateValidationCallback.

Is it safe to use the attached System.dll with this version of Monotouch?

Any plans when this issue will be corrected in the downloadable verison of Monotouch?
Comment 9 René Ruppert 2012-09-18 05:51:28 UTC
Are you sure you are talking about 5.0.4 and not 5.4?
In 5.4 I cannot see a regression. Validation is working as expected.
Comment 10 David 2012-09-18 06:06:17 UTC
Yes i was speaking of 5.4, my bad sorry.

I override the callback with a custom function to validate the certificates using BouncyCastle (its a requirement) and the X509Chain is always null.

ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateCertificates);

This ValidateCertificates expects as parameters the same parameters as the callback but the chain that is supposed to contain all the certificates in the certificate path is always null.
Comment 11 René Ruppert 2012-09-18 06:12:08 UTC
To my knowledge the chain will always be NULL and is as designed - Sebastien will have to comment on this. However, you should get proper error codes in the sslPolicyErrors parameter. This was the original bug and this is still fixed in 5.4.
Comment 12 Sebastien Pouliot 2012-09-18 08:25:22 UTC
There's a few reasons why the X509Chain is not created anymore:

* the Mono certificate stores do not exists (on MonoTouch) so it cannot be used to "influence" the building of the chain (not that iOS would take it into account for MonoTouch);

* there's no guarantee that the X509Chain content would match the one built internally by the operating system (i.e. the one we act from). This means you would override the OS decision based on different input data;

* they were never complete chains (since servers don't send the roots certificates), i.e. it always returned (at least) one error;

* the chains are CPU/memory intensive to process - even more considering it's not something that we can use / act on.

Note that the attached System.dll is much too old to be safely used with 5.4.

If you have a specific scenario in mind please open a new bug report and describe what/how you're doing your validation. We will look to see if there are ways this could be better handled (without introducing a penality for every other usage).