Bug 6501 - Unexpected invalid certificate exception
Summary: Unexpected invalid certificate exception
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.2.x
Hardware: All Other
: --- normal
Target Milestone: ---
Assignee: Bugzilla
Depends on:
Reported: 2012-08-10 10:54 UTC by info
Modified: 2013-02-11 12:45 UTC (History)
3 users (show)

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

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 info 2012-08-10 10:54:23 UTC
When attempting to connect to the URL "https://kreditkarten-banking.lbb.de/" via HttpWebRequest an unexpected invalid certificate exception is thrown:

System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b0109
  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 
  --- End of inner exception stack trace ---
  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
  at System.Net.HttpWebRequest.GetResponse () [0x00000] in <filename unknown>:0 
  at Subsembly.Util.HttpClient.GetResponse (System.Net.HttpWebRequest aRequest, System.String& sContentType) [0x0003e] in c:\Code\Subsembly_Banking\BotBanking\Util\HttpClient.cs:330 

This exception is unexpected because the very same code works with the very same URL on MonoTouch (iOS 5.1) and also on Windows Desktop with .NET Framework 2.0 without throwing an exception.
Comment 1 info 2012-08-10 10:56:28 UTC
I forgot: I am using the latest Mono for Android 4.2.5.
Comment 2 Jonathan Pryor 2012-08-10 11:29:11 UTC
Question: Can you connect to your URL from the Android Browser app _without_ getting any dialogs to manually accept the certificate? Which Android version is this?

I'm unable to reproduce your error on my Galaxy Nexus with Android 4.1/Jelly Bean.

Mono for Android uses the underlying Android OS for certificate validation. If the Browser app shows a dialog asking for manual acceptance of the certificate, then Mono for Android will reject the certificate as invalid (as Android will be rejecting the certificate). If this is the case, you will need to manually handle the certificate via the ServicePointManager.ServerCertificateValidationCallback property:


 - Jon
Comment 3 info 2012-08-10 11:39:49 UTC
I tested this on the Emulator with API Level 8 and on my LG Optimus 2X with Android 2.3.4. When entering the URL "https://kreditkarten-banking.lbb.de/" in the Android Browser on my LG, the web page opens without any prompt.
Comment 4 Jonathan Pryor 2012-08-10 15:55:30 UTC
One apparent oddity: if I open the site on an API 8 emulator within Browser, hit Menu > More > Page info, the top of the page info dialog says "Error." Click View Certificate, and Android says it's valid.

Weird. Also, completely unrelated to the problem at hand.
Comment 5 Jonathan Pryor 2012-08-10 16:19:36 UTC
Here's the problem:


ServicePointManager.ValidateChain() is invoked as part of SSL validation, and it invokes monodroidCallback() on Android. If/when that fails, it invokes ServicePointManager.ServerCertificateValidationCallback.

Here's the interesting bit: the `certs` collection is ~directly visible in the callback parameters, so we can see what's happening outside the confines of Android:

  // ServicePointManager.ServerCertificateValidationCallback
  using System;
  using System.Net;
  using System.IO;
  using System.Net.Security;
  using System.Security.Cryptography.X509Certificates;

  class Test {
    public static void Main ()
      ServicePointManager.ServerCertificateValidationCallback = Validator;
      string url = "https://kreditkarten-banking.lbb.de/";
      var request = (HttpWebRequest) WebRequest.Create(url);
      request.Method = "GET";
      var response = (HttpWebResponse) request.GetResponse ();
      int len = 0;
      using (var _r = new StreamReader (response.GetResponseStream ())) {
        char[] buf = new char [4096];
        int n;
        while ((n = _r.Read (buf, 0, buf.Length)) > 0) {
          /* ignore; we just want to make sure we can read */
          len += n;
      Console.WriteLine ("read: {0} bytes", len);

    static bool Validator (object sender, X509Certificate certificate,
        X509Chain chain, SslPolicyErrors sslPolicyErrors)
      Console.WriteLine ("Validator!");
      Console.WriteLine ("certificate: {0}", certificate);
      Console.WriteLine ("chain[0]: {0}", chain.ChainElements[0].Certificate);
      string a = certificate.ToString ();
      string b = chain.ChainElements [0].Certificate.ToString ();
      if (a == b)
        Console.WriteLine ("equal!");
      return true;

An interesting thing is apparent: the Validator() `certificate` parameter is equal to the `chain.ChainElements[0].Certificate` value. This means that in the internal Mono.Security.X509.X509CertificateCollection collection, the "leaf" certificate is duplicated, and thus when we ask Android "is this collection of certificates valid?", the collection we pass similarly has a duplicate entry.

Therein lies the problem: on some versions of Android (API8 in particular; I haven't tested all emulators), the duplicate certificate cause Android to report an error, which is why you see the error. (Rephrased: you get an error because Android doesn't like the certificate chain mono provides.)

API15 and API16, meanwhile, accept the "duplicate" entry, which is why it works for me on my Galaxy Nexus.
Comment 6 Jonathan Pryor 2012-08-10 16:24:22 UTC
I foresee three solutions:

1. Don't ask Android to validate the entire chain, have Android validate just the leaf entry (corresponding to the ServicePointManager.ServerCertificateValidationCallback `certificate` parameter).

This works, in this case. The problem is that I don't now if it'll work in the general case, as it's entirely possible for HTTPS servers to provide all intermediate certificates. If we only validate the leaf, and one of the intermediate certs isn't installed on the device, things will fail, even if the HTTPS server provides the intermediates.

I don't like this solution.

2. Assume that the leaf will always be duplicated in the X509Chain, and just marshal the chain.

This also works in my limited testing, but I don't see any documentation anywhere ensuring that this should be the case. (MSDN is rather lacking here. :-/

3. Check for "duplicate" certificate values, and only pass non-duplicate values to Java.
Comment 7 Jonathan Pryor 2012-08-10 16:39:27 UTC
WORKAROUND: Here is a method you can hookup to ServicePointManager.ServerCertificateValidationCallback which implements solution (3), allowing things to work on an API8 emulator:

  static bool Validator (object sender,
      System.Security.Cryptography.X509Certificates.X509Certificate certificate,
      System.Security.Cryptography.X509Certificates.X509Chain chain,
      System.Net.Security.SslPolicyErrors sslPolicyErrors)
    var sslTrustManager = (IX509TrustManager) typeof (AndroidEnvironment)
      .GetField ("sslTrustManager",
          System.Reflection.BindingFlags.NonPublic |
      .GetValue (null);
      Java.Security.Cert.X509Certificate> c = (f, v) =>
        f.GenerateCertificate (
            new System.IO.MemoryStream (v.GetRawCertData ()))
    var cFactory = Java.Security.Cert.CertificateFactory.GetInstance (
    var certs = new List<Java.Security.Cert.X509Certificate>(
        chain.ChainElements.Count + 1);
    certs.Add (c (cFactory, certificate));
    foreach (var ce in chain.ChainElements) {
      if (certificate.Equals (ce.Certificate))
      certificate = ce.Certificate;
      certs.Add (c (cFactory, certificate));
    try {
      sslTrustManager.CheckServerTrusted (certs.ToArray (),
      return true;
    catch (Exception e) {
      return false;
Comment 8 Jonathan Pryor 2012-08-13 14:35:56 UTC
Fixed in d4b59e0d.
Comment 9 Jonathan Pryor 2012-08-13 14:40:20 UTC
Correction: Comment #5 implied that the error was due to duplicate certificates. That may be the problem, but there is another problem as well: `certs` contains an _unordered_ collection of certificates. The ordering is "fixed" into an ordered into a "certificate chain" when the X509Chain is built. The result is that, for https://kreditkarten-banking.lbb.de/, `certs` and `chain` have a different ordering of certificates, and  Android API8 requires the correct ordering in order for things to work as well.

SSL is hard; let's flee in terror!
Comment 10 kenny 2013-02-08 17:44:34 UTC

tl;dr use the explicit type of CertificateFactory in your "getInstance" request which should be "X.509"

That code snippet above may throw an exception. You're getting the default type of TrustManagerFactory and using it to request an instance of a CertificateFactory. I think it was an error that the default TrustManagerFactory used to return "X.509"  If you look at the Standard Names documentation, you'll see the only supported algorithm is listed as "PKIX" for TrustManager: http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#TrustManagerFactory
Comment 11 Jonathan Pryor 2013-02-11 12:45:18 UTC
@kenny: Thank you for pointing that out. The 4.8+ release will explicitly use the X.509 provider instead of the TrustManagerFactory.DefaultAlgorithm provider.