• [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法


     

    编写者:郑昀@UltraPower

    关键字:HttpWebRequest,

    SSL,X509Certificate

    dotNet  Framwork 1.1

    编写时间:2005-3-29

    WSE 2.0 SP3

     

    目的:

    对于用HttpWebRequest加载证书请求远端https服务器时,发生的

    “基础连接已经关闭: 无法与远程服务器建立信任关系。”/

    The underlying connection was closed. Could not establish a secure SSL/TLS connection”错误,我们可以用如下方式解决。

    重现:

    使用以下代码,你就可以得到这个错误“基础连接已经关闭: 无法与远程服务器建立信任关系”:

    using System;

    using System.Text;

    using System.Net;

    using System.IO;

    using System.Security.Cryptography.X509Certificates;

     

    using Microsoft.Web.Services2.Security;

    using Microsoft.Web.Services2.Security.Tokens;

    using Microsoft.Web.Services2.Security.X509;

     

    static void Main(string[] args)

            {

                StringBuilder sb=new StringBuilder();

                string _strToRequest = "send";

     

                try

                {

                    //POST请求开始

                    byte[] bt=Encoding.Default.GetBytes("send");

                    HttpWebRequest Req=(HttpWebRequest)System.Net.WebRequest.Create("https://202.108.CCC.XXX:Port//");

                    Req.KeepAlive=true;

                    //Req.Timeout=60000;

                    Req.ContentType="text/xml";

                    Req.ContentLength=_strToRequest.Length;

                    Req.Method="POST";

                    X509CertificateStore store = X509CertificateStore.CurrentUserStore( X509CertificateStore.MyStore );

                    store.OpenRead();

        

                    //读取证书的keyid

                    Microsoft.Web.Services2.Security.X509.X509CertificateCollection certs =

                        store.FindCertificateByKeyIdentifier( Convert.FromBase64String( "CXv+xZ78zI3qWHGJ6Wh9BF6B23A=" ) );

                    X509SecurityToken token = null;

                    if (certs.Count > 0)

                    {

                        // 得到证书存储区的第1个人证书

                        token = new X509SecurityToken( ((Microsoft.Web.Services2.Security.X509.X509Certificate) certs[0]) );

                    } 

                    if(token != null)

                        Req.ClientCertificates.Add(token.Certificate);

                    Req.KeepAlive=true;

     

                    Stream ReqStream=Req.GetRequestStream();

                    ReqStream.Write(bt,0,bt.Length);

                    ReqStream.Close();

                    //得到响应

                    HttpWebResponse res=(HttpWebResponse)Req.GetResponse();

                    StreamReader sr=new StreamReader(res.GetResponseStream(),Encoding.Default);

                    sb.Append(sr.ReadToEnd());

                    res.Close();

                    sr.Close();

                }

                catch(Exception ex)

                {    

                    sb.Remove(0,sb.Length);

                    sb.Append("<?xml version=\"1.0\" encoding=\"gb2312\"?>\n");

                    sb.Append("<slia ver=\"1.0.0\">\n");

                    sb.Append("<result resid=\"501\">"+ex.Message+"</result>\n");

                    sb.Append("</slia>\n");

                }

     

                Console.WriteLine(sb.ToString());

     

                Console.Read();

            }

    原因:

    在“http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/cpguide/html/cpconhostingremoteobjectsininternetinformationservicesiis.asp”提到:

    证书标识特定的计算机,该计算机的名称位于证书的公共名称中。但是,很容易就会更改计算机的名称或使用客户端配置文件中的“localhost”,这会在客户端和服务器证书中的公共名称之间造成不匹配的情况。在 .NET Framework 1.0 版中,这一不匹配的情况将被忽略,并且将在服务器上引发调用。

    .NET Framework 1.1 版开始,这一不匹配的情况会引发以下异常:“System.Net.WebException:基础连接已经关闭:无法与远程服务器建立信任关系”。如果您无法配置远程处理客户端以使用证书公共名称,则可以使用客户端应用程序配置文件中的以下设置重写这一不匹配的情况。

    <system.net>

       <settings>

          <servicePointManager

             checkCertificateName="true"

          />

       </settings>

    </system.net>

    若要以编程方式使客户端忽略证书名称不匹配,客户端必须创建一个特定类的实例,如果 certificateProblem 值为 0x800c010f,该类将实现 ICertificatePolicy 接口并实现 CheckValidationResult 方法以返回 true。然后,您必须将该对象注册到 System.Net.ServicePointManager 对象,方法是将该对象传递到 ServicePointManager.CertificatePolicy 属性。”

    解决之道:

    但是用它列出的代码还是不对,我们改为CheckValidationResult无条件返回true即可。如下所示声明一个TrustAllCertificatePolicy类:

     

    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy

            {

                public TrustAllCertificatePolicy()

                {}

     

                public bool CheckValidationResult(ServicePoint sp,

                    System.Security.Cryptography.X509Certificates.X509Certificate cert,

                    WebRequest req, int problem)

                {

                    return true;

                }

            }

    然后,在请求之前加上

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

    即可。

    这样,代码就可以顺利和https服务器建立SSL通道了。

    编写者:郑昀@UltraPower

     

  • 相关阅读:
    Mysql 索引原理《一》索引原理与慢查询2
    Mysql 索引原理《一》索引原理与慢查询1
    Mysql内置功能《六》流程控制
    Mysql内置功能《五》 函数
    Mysql内置功能《四》存储过程
    Mysql pymysql模块
    HDU2020 绝对值排序
    HDU2019 数列有序
    HDU2018 母牛的故事
    HDU2016 数据的交换输出
  • 原文地址:https://www.cnblogs.com/zhengyun_ustc/p/135821.html
Copyright © 2020-2023  润新知