编写者:郑昀@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=newStreamReader(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