【问题标题】:Problem SSL Certificate C#问题 SSL 证书 C#
【发布时间】:2011-04-19 07:35:52
【问题描述】:

在我的 C# 应用程序中,我必须通过 https 调用 Web 服务并使用我已经拥有的 .crt 文件进行验证。这是满足此类需求的正确解决方案。一旦我得到一个可行的解决方案,我就更新了这篇文章,认为它可能会帮助像我这样的其他人。

解决方案: 下面的代码在整个应用程序执行中只需要执行一次。有了这个,我们设置了 ServerCertification 和 SSL 属性,在调用请求时将使用它们:

        public static void setSSLCertificate()
    {
        clientCert = new X509Certificate2(AUTHEN_CERT_FILE); // Pointing to the .crt file that will be used for server certificate verification by the client
        System.Net.ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);
    }

    public static bool customXertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPoicyErrors)
    {
        switch (sslPoicyErrors)
        {
            case System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable:
                break;
        }

        return clientCert.Verify();  // Perform the Verification and sends the result
    }

请求正常完成,就像我们没有实施 SSL 一样。这是一个 Post 请求代码:

        private static String SendPost(String uri, String post_data)
    {
        String resData = "";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.KeepAlive = false;
        request.ProtocolVersion = HttpVersion.Version10;
        request.ContentType = "application/x-www-form-urlencoded";
        request.Method = "POST";

        // turn request string into byte[]
        byte[] postBytes = Encoding.ASCII.GetBytes(post_data);

        Stream requestStream = null;

        try
        {
            // Send it
            request.ContentLength = postBytes.Length;
            requestStream = request.GetRequestStream();
            requestStream.Write(postBytes, 0, postBytes.Length);
        }
        catch (WebException we)
        {   // If SSL throws exception that will be handled here
            if (we.Status == WebExceptionStatus.TrustFailure)
                throw new Exception("Exception Sending Data POST : Fail to verify server " + we.Message);
        }
        catch (Exception e)
        {
            throw new Exception("Exception Sending Data POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (requestStream != null)
                requestStream.Close();
        }

        // Get the response
        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
            if (response == null)
                return "";
            StreamReader sr = new StreamReader(response.GetResponseStream());
            resData = sr.ReadToEnd().Trim();
            sr.Close();
        }
        catch (Exception e)
        {
            throw new Exception("Error receiving response from POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (response != null)
                response.Close();
        }

        return resData;
    }

特别感谢 Dipti Mehta,他的解释通过接受服务器证书帮助我在很大程度上实现了目标。她帮我解决了我的困惑。我终于找到了如何通过客户端使用 .crt 文件来验证服务器证书。

希望这对某人有所帮助。

谢谢

【问题讨论】:

    标签: c# exception ssl-certificate webexception


    【解决方案1】:

    当您浏览 HTTPS 站点时,您可能会看到一个对话框窗口,询问您是否要信任网络服务器提供的证书。因此接受证书的责任由用户承担。让我们回到 Web 服务场景,如果您想调用位于使用 SSL 和 HTTPS 的 Web 服务器上的 Web 服务,则会出现问题。

    从代码调用时,没有弹出对话窗口,询问是否信任证书;可能你会得到以下异常:

    “System.Net.WebException”类型的未处理异常发生在 系统.dll

    附加信息:底层连接已关闭:可能 不与远程服务器建立信任关系。

    但是这个问题有一个解决方案,你可以在你的代码中通过创建你自己的 CertificatePolicy 类来解决这个问题(它实现了ICertificatePolicy 接口)。在此类中,您必须编写自己的 CheckValidationResult 函数,该函数必须返回 true 或 false,就像您在对话窗口中按是或否一样。出于开发目的,我创建了以下接受所有证书的类,因此您不会再收到讨厌的 WebException:

    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
    {
      public TrustAllCertificatePolicy() 
      {}
    
      public bool CheckValidationResult(ServicePoint sp, X509Certificate cert,WebRequest req, int problem)
      {
        return true;
      }
    }
    

    如您所见,CheckValidationResult 函数始终返回 true,因此所有证书都是可信的。如果你想让这个类更安全一点,你可以使用 X509Certificate 参数添加额外的检查。要使用此 CertificatePolicy,您必须告诉 ServicePointManager 使用它:

    System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
    

    这必须在调用您的网络服务之前完成(在应用程序生命周期中的一次)。

    【讨论】:

    • 你也可以使用WCF为你做验证,查看weblogs.asp.net/hernandl/archive/2009/04/21/…
    • @user714721,感谢您提供完美而直接的解释。好的,所以有了这个,如果我想使用我的 .crt 文件,我必须在 CheckValidationResult 中针对从服务器收到的证书执行验证。清清楚楚到现在。那么 request.ClientCertificates 属性有什么用。我加不加,加ServicePointManager后没有区别。另外,我在每次请求 Web 服务时调用的 SendPost() 中添加了此代码。我必须从 SendPost() 中删除它,并且只允许它一次。你能帮忙处理一下ClientCertificates吗?谢谢
    • 是的,只需执行一次。 ServicePointManager 验证各种证书。但是,如果只想验证某些证书,那么您也可以指定,在这种情况下,request.ClientCertificates 将很有用
    • 我想用我拥有的证书 (.crt) 进行验证。在 CheckValidationResult 中,使用了您的代码,并添加了 .ClientCertificates.Add(x509cert2Obj)。但它不会通过通过的证书进行验证。我添加/删除上面的行没有区别,我可以访问网络服务。我尝试使用 new(字符串文件名)和 .Import(文件名)创建 X509Certificate2 对象。但没有一个有效。它的响应没有区别。据我了解,在添加 CertificatePolicy 后,如果我想验证证书,我必须添加 ClientCertificates。我哪里错了
    • 嗨,Dipti,你能帮帮我吗,因为我有非常相似的问题,我非常需要..我的代理服务器应用程序拒绝 HTTPS 请求。
    【解决方案2】:

    嗨电视,

    我不确定您提供的解决方案实际上是否是该问题的有效解决方案。此外,您的一些关于HttpWebRequest.ClientCertificates 的cmets 也表明了这一点。

    首先,区分服务器验证客户端证书和客户端验证服务器证书很重要。集合HttpWebRequest.ClientCertificates 用于向服务器发送客户端证书,以便服务器可以验证客户端是谁。您的问题(据我了解)是如何针对本地存储在客户端的证书验证未通过默认验证的服务器证书(例如自签名证书)。

    在这种情况下,解决方案确实是使用System.Net.ServicePointManager.ServerCertificateValidationCallback 并提供自定义验证。但是,您的验证方法似乎是错误的:它验证本地证书并且不关心服务器发送的证书。我会使用的是这样的:

    public static bool customXertificateValidation(
        Object sender, X509Certificate certificate, 
        X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;
    
        return clientCert.Equals(certificate);
    };
    

    此方法确保如果服务器证书通过默认验证(无错误),它将接受它,然后将证书的本地客户端副本与服务器提供的证书进行比较。只有当 Equals 测试通过时,客户端才能安全地继续。

    【讨论】:

      【解决方案3】:

      “验证”是指身份验证?在这种情况下,一个 .crt 是不够的,它只包含一个公钥。您需要私钥来验证自己并将其放入 ClientCertificates。您可以从 .pfx 文件中读取一个或将其导入证书容器并从那里使用它。

      【讨论】:

        【解决方案4】:

        由于 System.Net.ICertificatePolicy 已被弃用,我认为正确的做法应该是创建一个 RemoteCertificateValidationCallback 委托:

          void Awake()
            {
                System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateCertification;
            }
        
            void OnDestroy()
            {
                ServerCertificateValidationCallback = null;
            }
        
            public static bool ValidateCertification(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
            {
                print("VALIDATE!");
                return true;
            }
        

        【讨论】:

          猜你喜欢
          • 2017-09-07
          • 2015-05-29
          • 1970-01-01
          • 1970-01-01
          • 2013-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多