主页 > 编程资料 > C# >
发布时间:2015-09-26 作者:网络 阅读:230次

编写者:郑昀@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("\n");

 

                sb.Append("\n");

 

                sb.Append(""+ex.Message+"\n");

 

                sb.Append("\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:基础连接已经关闭:无法与远程服务器建立信任关系”。如果您无法配置远程处理客户端以使用证书公共名称,则可以使用客户端应用程序配置文件中的以下设置重写这一不匹配的情况。

 

 

  

 

     

 

         checkCertificateName="true"

 

      />

 

  

 

 

若要以编程方式使客户端忽略证书名称不匹配,客户端必须创建一个特定类的实例,如果 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

 


 


Feedback
# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-04-16 01:41 by lonelystranger
Nice!

I have read your articles on CSDN before.
# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-07-28 14:18 by caravarn
TrustAllCertificatePolicy 这个类具体怎么用啊,我还是
不明白我的 msn :bobomail@citiz.net

# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-07-28 20:19 by bobomail
store.FindCertificateByKeyIdentifier( Convert.FromBase64String( "CXv+xZ78zI3qWHGJ6Wh9BF6B23A=" ) );
这句话 里的CXv+xZ78zI3qWHGJ6Wh9BF6B23A=" 就是证书号码
这个在程序里怎么制定
很急啊 msn : bobomail@citiz.net

# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-07-29 00:11 by 让变化成为计划的一部分
Hi,
如果你链接的服务器并不要求你用证书,虽然你要走https,其实读取证书的keyid 是不需要的。后来我们就没有取,直接做请求,只要有
System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
这句话,就可以了。

至于key id ,是通过WSE .20中的X.509 证书工具查看的.


发件人: ZhengYun
收件人: 'bobomail@citiz.net'
主题: 代码如下所示


using Microsoft.Web.Services2.Security;
using Microsoft.Web.Services2.Security.Tokens;
using Microsoft.Web.Services2.Security.X509;

//POST请求开始
byte[] bt=Encoding.Default.GetBytes(_strToRequest);
HttpWebRequest Req=(HttpWebRequest)System.Net.WebRequest.Create(_Url);
Req.KeepAlive=true;
Req.ContentType="txt/xml";

Req.Method="POST";

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

Stream ReqStream=Req.GetRequestStream();
.......................

internal class TrustAllCertificatePolicy:ICertificatePolicy
{
public TrustAllCertificatePolicy()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
public bool CheckValidationResult(ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert,System.Net.WebRequest req, int problem)
{
return true;// 这是最关键的,返回true,告诉客户端忽略证书名称不匹配!
}
}


# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-08-01 15:04 by caravarn
我请求的服务器是用java 开发 System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy(); 调试中也 return true
但还是提示连接失败?
这是原因呢?

bobomail@citiz.net

# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-08-17 11:14 by caravarn
返回的错误
Problem code 0x800B0109
Problem code 0x800B0101
Problem code 0x800B010F
Problem code 0x00000000

对应的错误
public enum CertificateProblem : long
{
CertEXPIRED = 0x800B0101,
CertVALIDITYPERIODNESTING = 0x800B0102,
CertROLE = 0x800B0103,
CertPATHLENCONST = 0x800B0104,
CertCRITICAL = 0x800B0105,
CertPURPOSE = 0x800B0106,
CertISSUERCHAINING = 0x800B0107,
CertMALFORMED = 0x800B0108,
CertUNTRUSTEDROOT = 0x800B0109,
CertCHAINING = 0x800B010A,
CertREVOKED = 0x800B010C,
CertUNTRUSTEDTESTROOT = 0x800B010D,
CertREVOCATION_FAILURE = 0x800B010E,
CertCN_NO_MATCH = 0x800B010F,
CertWRONG_USAGE = 0x800B0110,
CertUNTRUSTEDCA = 0x800B0112
}

这个不知道怎么解决阿

# re: [C#]用HttpWebRequest加载证书建立SSL通道时发生异常的解决办法   
2005-08-18 09:45 by 让变化成为计划的一部分
错误号:0x800B0109的意思是:
A certificate chain processed, but terminated in a root certificate which is
not trusted by the trust provider

也就是说,你的CA Cert并没有被对方所信任。你不觉得很奇怪吗?你装对方站点提供的证书了吗?
至于0x800b0101的意思是 :A required certificate is not within its validity period when
verifying against the current system clock or the timestamp in the signed
file.


那么请你确定几点:

1:询问对方是否需要证书?

2:你的时钟和对方的时间是否差距在5分钟之内?

3:这种现象如果换一台测试机器,是否依然出现呢?


 

关键字词: