【问题标题】:Mono https webrequest fails with "The authentication or decryption has failed"Mono https webrequest 失败并显示“身份验证或解密失败”
【发布时间】:2011-06-23 00:54:15
【问题描述】:

我正在制作一个简单的 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 上运行吗?
  • 正如我的问题中所解释的,我在 Windows/.NET 中尝试过,但是我从未在 Windows 上的 Mono 中尝试过。看看它是否有效会很有趣。但是,jpobst 的帖子似乎可以解决我的问题。
  • 在这里拖死人——我错过了什么吗? jpobst 的帖子在哪里?
  • @daveL:由于人们可以更改他们的显示名称,它可能仍然存在(但由于我也只是这个问题的访问者,所以我无法告诉你它是哪一个)。跨度>

标签: c# https mono webrequest


【解决方案1】:

我在使用 Unity(也使用单声道)时遇到了同样的问题,this post 帮助我解决了这个问题。

在提出请求之前添加以下行:

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
  • 像魅力一样工作。 (Y)
  • 有一个潜在的问题是chain.Build(cert) 总是在单声道中返回 true,即使请求 bad ssl
  • 抱歉,您的解决方案是使用在 Internet 上找到的随机代码滚动您自己的 SSL 证书验证?如果您不关心 SSL 提供的安全性,只需使用您的函数return true;。这只是“大力挥手的安全性”,确实完全被破坏了,并且很乐意接受损坏或欺骗的证书。
【解决方案2】:

Windows 上的 .NET Framework 使用 Windows 证书存储(mmc、添加/删除管理单元、证书)来确定是否接受来自远程站点的 SSL 证书。 Windows 附带一堆根证书颁发机构和中间证书颁发机构 (CA),它们会通过 Windows 更新定期更新。因此,您的 .NET 代码通常会信任由证书存储中的 CA 或 CA 的后代颁发的证书(包括大多数著名的商业 CA)。

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

看这里:

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

【讨论】:

  • 此处简要说明 Entrust 的 G2 根证书,该证书目前不包含在 Mozilla 的 CA 存储中。他们计划在 Firefox 38 版本中添加它,但这不能保证。目前,如果您使用 Mozilla 的 CA 存储,您的代码将无法验证使用 G2 根证书签名的证书。 (指纹在 entrust.net/developer,来自 2013 的 Mozilla 错误在 bugzilla.mozilla.org/show_bug.cgi?id=849950
【解决方案3】:

在发出请求 http 请求之前写下这一行。这应该可行。

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

【讨论】:

  • 请考虑包含一些关于您的答案的信息,而不是简单地发布代码。我们试图提供的不仅仅是“修复”,而是帮助人们学习。您应该解释原始代码中的问题、您所做的不同之处以及您的更改为何有效。
  • 这违背了合理的网络安全性:您只是在没有首先验证的情况下接受任何证书。请参阅@Ludovic 的答案,了解在接受之前验证的功能。
  • 这会拒绝 SSL 错误的子类,但会默默地允许其他错误(例如自签名证书)。因此(就像这里的其他答案一样),这有效地禁用了 SSL 的所有安全优势。如果这就是您想要的,只需return true;
【解决方案4】:

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

【讨论】:

  • 似乎最近版本的mono真的很喜欢确认,所以对于那些不想多次输入“yes”的人来说,这可以工作:yes "yes" | ./mozroots --import --quiet当放置在mono的bin文件夹中时.在 macOS 上,这将是 /Library/Frameworks/Mono.framework/Versions/Current/bin
  • 这是否适用于不存在 firefox 的服务器?
【解决方案5】:

我遇到了同样的问题。 当 http 响应抛出此异常时,我会这样做:

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

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

    • 您可以将此答案简化为一行:ServicePointManager.ServerCertificateValidationCallback += (p1, p2, p3, p4) =&gt; true;
    【解决方案7】:

    我也遇到了这个错误。

    我尝试了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();
        }
    }
    

    【讨论】:

      【解决方案8】:

      第一个答案已经说明了:Windows 以外的任何东西上的 Mono 都没有附带任何东西,所以最初它不信任任何证书。那么该怎么办呢?

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

      简短的总结: 您可以:

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

      上面的链接带有每个案例的代码示例。

      【讨论】:

      • 调用 mozroots --import --sync 应该可以通过将 mozilla 中的默认证书添加到单声道证书存储中来为您解决此问题。
      【解决方案9】:

      根据接受的答案导入证书后,我仍然遇到此问题。

      我发现 support for TLS 1.2 was added in Mono 4.8.0 使用 Google 的 BoringSSL,并且我使用的 Mono 版本比这更早。我更新到 Mono 5.10,现在可以连接而不会收到此异常。

      【讨论】:

        【解决方案10】:

        您可以在 iOS Build 中设置 Mono TLS 实现,并且一切都将正常工作,如下所述:http://massivepixel.co/blog/post/xamarin-studio-6-certificate-unknown(尽管 Mono TLS 不支持较新版本的 TLS,但我还没有遇到过它是问题)。

        【讨论】:

          猜你喜欢
          • 2013-04-22
          • 2014-12-28
          • 2018-05-09
          • 1970-01-01
          • 2021-11-23
          • 1970-01-01
          • 2021-06-14
          相关资源
          最近更新 更多