Mono https webrequest失败,并显示“身份验证或解密失败”


71

我正在制作一个简单的REST客户端以在我的C#应用​​程序中使用。在Windows的.net中,它与http://和https://连接一起使用时效果很好。在Ubuntu 10.10上的Mono 2.6.7中(也用2.8测试了相同的结果),只有http://可以工作。https://连接在request.GetResponse()方法上引发此异常:

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 

我还没有找到任何解决此问题的方法。任何人都知道为什么会发生这种情况以及如何解决它?

同样,这仅在Mono中失败,.Net建立连接似乎没有任何问题。

这是调用代码:

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);
    }
}

您是否尝试在Windows / Mono,Windows / .NET上运行?
abatishchev 2011年

如我的问题所述,我在Windows / .NET中尝试过它,但是我从未在Windows的Mono中尝试过它。看看它是否有效将很有趣。但是,jpobst的帖子似乎可以解决我的问题。
乔尔

4
在这里拖死人-我错过了什么吗?jpobst的帖子在哪里?
戴夫·劳伦斯

@daveL:由于人们可以更改其显示名称,因此它可能仍在这里(但由于我也是该问题的访客,所以我无法告诉您它是哪个名称)。
pinkgothic '16

Answers:


6

默认情况下,Mono不信任任何证书,要导入Mozilla受信任的根权限,您可以mozroots --import --quiet在mozroots.exe所在的mono安装文件夹中运行


1
似乎最新版本的mono确实喜欢确认,因此对于那些不想多次键入“是”的用户来说,这可以起作用: yes "yes" | ./mozroots --import --quiet 将其放在mono的bin文件夹中。在MacOS这个尔德是/Library/Frameworks/Mono.framework/Versions/Current/bin
NOVER

这在不存在firefox的服务器上有效吗?
亚历杭德罗

50

我在Unity上也遇到了同样的问题(它也使用mono),这篇文章帮助我解决了这个问题。

在发出请求之前,只需添加以下行:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;

而这种方法:

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;
}

这实际上应该可以工作,但是在我的swagger自动生成的客户端中似乎不行。参见github.com/swagger-api/swagger-editor/issues/1034
loretoparisi

3
像魅力一样工作。(Y)
Emy Stats'Oct

有一个潜在的问题,chain.Build(cert)即使对错误的ssl发出请求,总是会在单声道请求中返回true 。
sakiM

3
抱歉,您的解决方案是使用Internet上的随机代码滚动自己的SSL证书验证?如果您不关心SSL所提供的安全性,则只需具有function即可。这只是“用力挥舞的安全性”,确实完全被破坏了,并且很乐意接受被破坏或欺骗的证书。return true;
索伦·勒夫堡

28

Windows上的.NET Framework使用Windows证书存储(mmc,添加/删除管理单元,证书)来确定是否接受来自远程站点的SSL证书。Windows附带了一堆根和中级证书颁发机构(CA),并且Windows Update会定期对其进行更新。因此,您的.NET代码通常将信任证书,前提是该证书是由证书存储中的CA或CA的后代颁发的(包括最知名的商业CA)。

在Mono中,没有Windows证书存储。Mono有自己的商店。默认情况下,它为空(没有可信任的默认CA)。您需要自己管理条目。

在这里看看:

默认安装后,mozroots.exe点将使您的单声道安装信任Firefox信任的所有内容。


1
关于Entrust的G2根证书的简短说明,当前未包含在Mozilla的CA存储中。他们计划在Firefox 38发行版中添加它,但不能保证。当前,如果您使用Mozilla的CA存储,则您的代码将无法验证使用G2根证书签名的证书。(指纹在entrust.net/developer上2013年的Mozilla bug在bugzilla.mozilla.org/show_bug.cgi?id=849950上
Benjamin Nolan

7

在make request http request之前写此行。这应该是可行的。

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;
    }

7
请考虑包括有关您的答案的一些信息,而不是简单地发布代码。我们不仅尝试提供“修复”,还帮助人们学习。您应该解释原始代码中的错误,所做的不同操作以及所做更改的原因。
Andrew Barber 2014年

4
这与合理的网络安全性背道而驰:您只是在接受任何证书而无需先对其进行验证。请参见@Ludovic的答案,以获取在接受之前已验证的功能。
ssamuel

1
这拒绝了SSL错误的子类,但静默允许其他错误(例如,自签名证书)。因此(像这里的其他答案一样),这有效地禁用了SSL带来的所有安全优势。如果那是您想要的,那就return true;
索伦Løvborg

4

我有同样的问题。当http响应抛出此异常时,我将执行以下操作:

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

这将导入丢失的证书,并且该异常不再发生。


4

Unity的另一个解决方案是将ServicePointManager初始化一次,以始终接受证书。这可行,但显然不安全。

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
       };

5
您可以将此答案简化为以下一行:ServicePointManager.ServerCertificateValidationCallback += (p1, p2, p3, p4) => true;
Andy

3

我也遇到错误。

我试了一下ServicePointManager.ServerCertificateValidationCallbackServicePointManager.CertificatePolicy但还是没用。

我很生气 构建一个cURL包装器。对于我的玩具项目来说,一切正常。

/// <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();
    }
}

3

第一个答案已经说过:Windows以外的任何版本上的Mono都不附带任何内容,因此最初它不信任任何证书。那么该怎么办?

这是一篇不错的文章,它从开发人员的角度介绍了解决问题的不同方法:http : //www.mono-project.com/archived/usingtrustedrootsrespectfully/

简短摘要:您可以:

  • 忽略安全问题
  • 忽略问题
  • 让用户知道并中止
  • 让用户知道并选择继续承担风险

上面的链接附带每种情况的代码示例。


1
调用mozroots --import --sync应该通过将来自mozilla的默认证书添加到单证书存储中来为您修复它。
ScottB


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.