Mono https webrequest fails with "The authentication or decryption has failed"

C#HttpsMonoWebrequest

C# Problem Overview


I'm making a simple REST client to use in my C# applications. In .net on Windows It works great with http:// and https:// connections. In mono 2.6.7 (Also tested with 2.8 with the same results) on Ubuntu 10.10 only http:// works. https:// connections throw up this exception on the request.GetResponse() method:

Unhandled Exception: 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: 0xffffffff800b010a
  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 (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
  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 

I haven't been able to find any way to fix this. Anyone have any idea why this is happening and how to fix it?

Again, this only fails in Mono, .Net doesn't seem to have any problem establishing a connection.

here's the calling code:

public JToken DoRequest(string path, params string[] parameters) {
    if(!path.StartsWith("/")) {
        path = "/" + path;
    }
    string fullUrl = url + path + ToQueryString(parameters);
    			
    if(DebugUrls) Console.WriteLine("Requesting: {0}", fullUrl);
    
    WebRequest request = HttpWebRequest.CreateDefault(new Uri(fullUrl));
    using(WebResponse response = request.GetResponse())
    using(Stream responseStream = response.GetResponseStream()) {
        return ReadResponse(responseStream);
    }
}

C# Solutions


Solution 1 - C#

I had the same problem with Unity (which also uses mono) and this post helped me to solve it.

Just add the following line before making your request:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;

And this method:

public bool MyRemoteCertificateValidationCallback(System.Object sender,
    X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
	bool isOk = true;
	// If there are errors in the certificate chain,
    // look at each error to determine the cause.
	if (sslPolicyErrors != SslPolicyErrors.None) {
		for (int i=0; i<chain.ChainStatus.Length; i++) {
			if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown) {
                continue;
            }
            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
			chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
			chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0);
			chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
			bool chainIsValid = chain.Build ((X509Certificate2)certificate);
			if (!chainIsValid) {
				isOk = false;
                break;
			}
		}
	}
	return isOk;
}

Solution 2 - C#

The .NET Framework on Windows uses the Windows Certificates store (mmc, Add/Remove Snap-Ins, Certificates) to determine whether to accept an SSL certificate from a remote site. Windows ships with a bunch of Root and Intermediate Certificate Authorities (CA) and they get updated periodically by Windows Update. As a result, your .NET code will generally trust a certificate provided it was issued by a CA or a descendant of a CA in the certificate store (most reputable commercial CA's are included).

In Mono, there is no Windows Certificate store. Mono has it's own store. By default, it is empty (there are no default CA's that are trusted). You need to manage the entries yourself.

Take a look here:

The mozroots.exe point will cause your mono install to trust everything that Firefox trusts after a default install.

Solution 3 - C#

Write this line before make request http request. this should be work.

ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
		

private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
	{
		//Return true if the server certificate is ok
		if (sslPolicyErrors == SslPolicyErrors.None)
			return true;
		
		bool acceptCertificate = true;
		string msg = "The server could not be validated for the following reason(s):\r\n";
		
		//The server did not present a certificate
		if ((sslPolicyErrors &
		     SslPolicyErrors.RemoteCertificateNotAvailable) == SslPolicyErrors.RemoteCertificateNotAvailable)
		{
			msg = msg + "\r\n    -The server did not present a certificate.\r\n";
			acceptCertificate = false;
		}
		else
		{
			//The certificate does not match the server name
			if ((sslPolicyErrors &
			     SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
			{
				msg = msg + "\r\n    -The certificate name does not match the authenticated name.\r\n";
				acceptCertificate = false;
			}
			
			//There is some other problem with the certificate
			if ((sslPolicyErrors &
			     SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
			{
				foreach (X509ChainStatus item in chain.ChainStatus)
				{
					if (item.Status != X509ChainStatusFlags.RevocationStatusUnknown &&
					    item.Status != X509ChainStatusFlags.OfflineRevocation)
						break;
					
					if (item.Status != X509ChainStatusFlags.NoError)
					{
						msg = msg + "\r\n    -" + item.StatusInformation;
						acceptCertificate = false;
					}
				}
			}
		}
		
		//If Validation failed, present message box
		if (acceptCertificate == false)
		{
			msg = msg + "\r\nDo you wish to override the security check?";
//			if (MessageBox.Show(msg, "Security Alert: Server could not be validated",
//			             MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
				acceptCertificate = true;
		}
		
		return acceptCertificate;
	}

Solution 4 - C#

Mono does not trust any certificate by default, to import the Mozilla trusted root authorities you can run mozroots --import --quiet in the mono installation folder where mozroots.exe is located

Solution 5 - C#

I had the same problem. When the http response throws this exception then I do:

System.Diagnostics.Process.Start("mozroots","--import --quiet");

this imports the missing certificates and the exception never happend again.

Solution 6 - C#

Another solution for Unity is to initialize the ServicePointManager once to always accept the certificates. This works but is obviously not secure.

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
           delegate (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                                   System.Security.Cryptography.X509Certificates.X509Chain chain,
                                   System.Net.Security.SslPolicyErrors sslPolicyErrors)
           {
               return true; // **** Always accept
       };

Solution 7 - C#

I encounter the error too.

I tryied ServicePointManager.ServerCertificateValidationCallback and ServicePointManager.CertificatePolicy but still not work.

I anger. build a cURL wraper. It's work fine for my toy project.

/// <summary>
/// For MONO ssl decryption failed
/// </summary>
public static string PostString(string url, string data)
{
    Process p = null;
    try
    {
        var psi = new ProcessStartInfo
        {
            FileName = "curl",
            Arguments = string.Format("-k {0} --data \"{1}\"", url, data),
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = false,
        };

        p = Process.Start(psi);

        return p.StandardOutput.ReadToEnd();
    }
    finally
    {
        if (p != null && p.HasExited == false)
            p.Kill();
    }
}

Solution 8 - C#

The first answer says it already: Mono on anything other than Windows doesn’t come with anything so initially it doesn’t trust any certificate. So what to do?

Here is a nice article about different ways to deal with the problem from the developer’s perspective: http://www.mono-project.com/archived/usingtrustedrootsrespectfully/

Short summary: You can either:

  • ignore the security concern
  • ignore the problem
  • let the user know and abort
  • let the user know and give him/her a choice to continue at own risk

The above link comes with code examples for each case.

Solution 9 - C#

I was still having this problem after importing the certificates as per the accepted answer.

I found that support for TLS 1.2 was added in Mono 4.8.0 which uses Google's BoringSSL, and that I was using a version of Mono older than this. I updated to Mono 5.10 and can now connect without receiving this exception.

Solution 10 - C#

You can set Mono TLS implementation in iOS Build and everything will work fine as described here: http://massivepixel.co/blog/post/xamarin-studio-6-certificate-unknown (though Mono TLS doesn't support newer versions of TLS but I haven't yet bumped on the issue that it is a problem).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoelView Question on Stackoverflow
Solution 1 - C#Ludovic FeltzView Answer on Stackoverflow
Solution 2 - C#scottt732View Answer on Stackoverflow
Solution 3 - C#Pawan ChaurasiyaView Answer on Stackoverflow
Solution 4 - C#LiriBView Answer on Stackoverflow
Solution 5 - C#harry4516View Answer on Stackoverflow
Solution 6 - C#Erik KalkokenView Answer on Stackoverflow
Solution 7 - C#IlPADlIView Answer on Stackoverflow
Solution 8 - C#GerhardView Answer on Stackoverflow
Solution 9 - C#Owen PaulingView Answer on Stackoverflow
Solution 10 - C#Ivan IčinView Answer on Stackoverflow