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

68,154

Solution 1

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 2

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 3

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 4

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 5

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
       };
Share:
68,154
Joel
Author by

Joel

I'm primarily a C# and Java developer. I also know Objective-C and have done some work with the iPhone SDK. I know some PHP, ASP.net, C++, and dabbled in some other languages.

Updated on July 05, 2022

Comments

  • Joel
    Joel almost 2 years

    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);
        }
    }
    
  • Andrew Barber
    Andrew Barber over 9 years
    Please consider including some information about your answer, rather than simply posting code. We try to provide not just 'fixes', but help people learn. You should explain what was wrong in the original code, what you did differently, and why your change(s) worked.
  • Benjamin Nolan
    Benjamin Nolan about 9 years
    A quick note here about Entrust's G2 root certificate, which is currently NOT included in Mozilla's CA store. They are planning to have it added for the release of Firefox 38, but that's not guaranteed. Currently, if you use Mozilla's CA store, your code will fail to verify certificates which are signed with the G2 root certificate. ( Thumbprints are at entrust.net/developer, Mozilla bug from 2013 is at bugzilla.mozilla.org/show_bug.cgi?id=849950 )
  • ScottB
    ScottB over 8 years
    calling mozroots --import --sync should fix it for you by adding the default certificates from mozilla into the mono certificate store.
  • ssamuel
    ssamuel almost 8 years
    This is contrary to reasonable web security: you're simply accepting any certificate without first validating it. See the answer by @Ludovic for a function that validates before accepting.
  • loretoparisi
    loretoparisi over 7 years
    This should work in fact, but in my swagger auto generated client seems not. See github.com/swagger-api/swagger-editor/issues/1034
  • Emy
    Emy over 7 years
    Worked like charm. (Y)
  • Andy
    Andy almost 7 years
    You can simplify this answer to one line: ServicePointManager.ServerCertificateValidationCallback += (p1, p2, p3, p4) => true;
  • sakiM
    sakiM about 6 years
    There is a potential problem that chain.Build(cert) always returns true in mono even request to a bad ssl.
  • Søren Løvborg
    Søren Løvborg almost 6 years
    Sorry, but your solution was to roll your own SSL certificate validation using random code found on the Internet? If you don't care about the security offered by SSL, just have your function return true;. This is just "security by vigorous handwaving", and is indeed completely broken and will gladly accept broken or spoofed certificates.
  • Søren Løvborg
    Søren Løvborg almost 6 years
    This rejects a subclass of SSL errors, but silently permits others (e.g. self-signed certs). Thus (like other answers here), this effectively disables all the security benefits by SSL. If that's what you want, just return true;.
  • nover
    nover over 5 years
    Seems like recent versions of mono really likes confirmation, so for those who do not wish to type "yes" a number of times, this can work: yes "yes" | ./mozroots --import --quiet When placed in the bin folder of mono. On macOS this wold be /Library/Frameworks/Mono.framework/Versions/Current/bin
  • Alejandro
    Alejandro over 3 years
    Does this works on a server where firefox doesn't exists?