Bug 18962 - On Android, SslStream authentication does not know about the system's default trusted roots, causing "Invalid certificate received from server"
Summary: On Android, SslStream authentication does not know about the system's default...
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.12.2
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Marek Habersack
Depends on:
Reported: 2014-04-11 17:05 UTC by Brendan Zagaeski (Xamarin Team, assistant)
Modified: 2014-07-07 14:46 UTC (History)
3 users (show)

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

Possible patch for TlsServerCertificate (1.58 KB, patch)
2014-04-11 17:12 UTC, Brendan Zagaeski (Xamarin Team, assistant)

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 Brendan Zagaeski (Xamarin Team, assistant) 2014-04-11 17:05:30 UTC
This is the same problem as bug #10366 (on iOS), but the bug also requires a platform-specific fix for Android.

## Steps to reproduce

1. Create a new Android application.

2. Add these three lines to create a TCP `SslStream` and attempt client authentication:
> var tcpClient = new TcpClient("google.com", 443);
> var ssl = new SslStream(tcpClient.GetStream(), false);
> ssl.AuthenticateAsClient("google.com");

3. Run the app.

## Result
> UNHANDLED EXCEPTION: System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server.
>   at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.LocalValidation (Mono.Security.Protocol.Tls.ClientContext context, AlertDescription description) [0x00000] in <filename unknown>:0 
>   at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0 
>   at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0 
>   at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0 
>   at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0 
>   at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
>   --- End of inner exception stack trace ---
>   at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 

## Possible fixes

I briefly tried both of these possibilities using patched versions of Mono.Security, and they both allow "google.com" to verify, while correctly rejecting sites with self-signed certificates.

### Possibility 1: add the Android trusted root certificates to `X509StoreManager.TrustedRootCertificates`

That is, add something like this to the getter for `TrustedRootCertificates`: 
> foreach (var javaCert in javaTrustManager.GetAcceptedIssuers ()) {
> 	trustedCerts.Add(new X509Certificate (javaCert.GetEncoded ()));
> }

... where `javaTrustManager` is an Android default `ITrustManager`. I think `Android.Runtime.AndroidEnvironment` already includes some methods to create an appropriate `ITrustManager` (used by `AndroidPlatform.TrustEvaluateSsl()`).

### Possibility 2: change `TlsServerCertificate.LocalValidation()` to more closely resemble `ServicePointManager.ValidateChain()` [1]

In particular, add a platform-specific step to un-set the "PartialChain" error if `AndroidPlatform.TrustEvaluateSsl()` returns `true`.

> [1] https://github.com/mono/mono/blob/43fcd744d0b7237fef50c85eca0d62245584aa7d/mcs/class/System/System.Net/ServicePointManager.cs#L456

Many thanks!
Comment 1 Brendan Zagaeski (Xamarin Team, assistant) 2014-04-11 17:12:01 UTC
Created attachment 6560 [details]
Possible patch for TlsServerCertificate

The `TrustedRootCertificates` fix seems like it might be preferable, but just in case it might be useful, here's the patch I came up with for the `TlsServerCertificate` option.
Comment 3 Sven Reißenweber 2014-04-15 18:39:31 UTC
additional when a new http or tcp client was initialized the ValidateServerCertificate callback method will never called again, only sometimes.

i think the last certificate is stored and there is not a method to reset it that the ValidateServerCertificate can check the certificate for the new https/ tcp client.

in our example means that. in the case a user try to login per tcp (SSL). he or she accept the certificate issues and cancel the login process. than he or she whant to login again, technical i start the new tcp client and there is no question for accept the remote certificate issues (if that is the same host).

this is ok if i can get the current accepted ssl Thumbprint. but even there is the next problem:

if i try to read:
new System.Security.Cryptography.X509Certificates.X509Certificate2 (currentConnectionState.sslStream.RemoteCertificate).Thumbprint

after AuthenticateAsClient sometimes it is successful and sometimes it is null

so what can i do?
Sven Reißenweber
Comment 4 Brendan Zagaeski (Xamarin Team, assistant) 2014-04-17 19:33:38 UTC
I've filed a separate bug #19141 for the callback problem from Comment 3.
Comment 5 Brendan Zagaeski (Xamarin Team, assistant) 2014-04-18 01:48:32 UTC
Just a little extra tidbit I noticed while writing up the other bug: I think the commit from bug 10366 [1] could be modified to fix the bug on Android too. As I understand it, the rough idea of that fix is to skip over the default certificate validation path (involving `TrustedRootCertificates`), and instead let `ServicePointManager.ChainValidationHelper.ValidateChain()` do most of the work. In my quick tests, this seems to have an added advantage of passing the full certificate chain to the SslStream's `RemoteCertificateValidationCallback`.

It looks like the main changes needed for this approach would be to add `MONODROID` to the `#if` directives, update the Makefile, and add the appropriate `Mono.Security` source files to `monodroid_System.dll.sources`.

> [1] Mono 96a8500795960ae1d9e81e28095c3e2dda303342
Comment 6 Marek Habersack 2014-04-25 16:18:02 UTC
The bug is fixed in the master branch. The relevant commits:

  XA: 362bb89c7c227a39d9e8fea17dd7939722050d98
  Mono: 34fb07d05febe08a5662bca68cdf132c3bbc08c1

The fix basically follows the suggestion made by Brendan in comment 5 - thanks Brendan!
Comment 7 Brendan Zagaeski (Xamarin Team, assistant) 2014-04-25 23:42:57 UTC
Very cool! Thanks!
Comment 8 Udham Singh 2014-07-07 14:46:44 UTC
I have checked this issue with code provided in bug description, now this is working fine. To check this issue I have followed the steps mentioned below :

1. Create a simple android application.
2. Implement the code provide in bug description.
3. Run the application and debug implemented code.

Screencast : http://www.screencast.com/t/GYTEPXA6

Environment Info :

=== Xamarin Studio ===

Version 5.2 (build 180)
Installation UUID: 3dbf10c4-ed30-4e55-8a8b-1704777c7b5f
	Mono 3.6.0 ((no/a653694)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 306000000

=== Apple Developer Tools ===

Xcode 5.1 (5084)
Build 5B130a

=== Xamarin.iOS ===

Version: (Business Edition)
Hash: 762ebac
Build date: 2014-07-03 15:53:37-0400

=== Xamarin.Android ===

Version: 4.14.0 (Business Edition)
Android SDK: /Users/apprpject/Desktop/android-sdk-macosx
	Supported Android versions:
		2.1   (API level 7)
		2.2   (API level 8)
		2.3   (API level 10)
		3.1   (API level 12)
		3.2   (API level 13)
		4.0   (API level 14)
		4.0.3 (API level 15)
		4.1   (API level 16)
		4.2   (API level 17)
		4.3   (API level 18)
		4.4   (API level 19)
Java SDK: /usr
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)

=== Xamarin.Mac ===


=== Build Information ===

Release ID: 502000180
Git revision: c27cfff0160e6f3d8ad82b57be030d6adf866743
Build date: 2014-07-04 18:06:04-04
Xamarin addins: 68026ad1ccee9923e927d9cfcca408d673d5ab61

=== Operating System ===

Mac OS X 10.8.5
Darwin localhost 12.5.0 Darwin Kernel Version 12.5.0
    Sun Sep 29 13:33:47 PDT 2013
    root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64