【发布时间】:2015-02-22 21:57:29
【问题描述】:
我正在尝试使用以下代码从手持设备(Windows CE / Compact 框架)调用 REST 方法:
public static HttpWebRequest SendHTTPRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
ExceptionLoggingService.Instance.WriteLog("Reached
fileXferREST.SendHTTPRequestNoCredentials");
WebRequest request = null;
try
{
request = WebRequest.Create(uri);
request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
request.ContentType = contentType;
((HttpWebRequest)request).Accept = contentType;
((HttpWebRequest)request).KeepAlive = false;
((HttpWebRequest)request).ProtocolVersion = HttpVersion.Version10;
if (method != HttpMethods.GET && method != HttpMethods.DELETE)
{
byte[] arrData = Encoding.UTF8.GetBytes(data);
request.ContentLength = arrData.Length;
using (Stream oS = request.GetRequestStream())
{
oS.Write(arrData, 0, arrData.Length);
}
}
else
{
request.ContentLength = 0;
}
}
catch (Exception ex)
{
String msgInnerExAndStackTrace = String.Format(
"{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException,
ex.StackTrace);
ExceptionLoggingService.Instance.WriteLog(String.Format("From
FileXferREST.SendHTTPRequestNoCredentials(): {0}", msgInnerExAndStackTrace));
}
return request as HttpWebRequest;
}
传递给方法的值是:
uri: "https://seastore.nrbq.ad/ggr.web/api/inventory/sendXML/duckbill/platypus/INV_3_20090313214959000.xml"
HttpMethods: HttpMethods.POST
data: [ some xml ]
contentType: "application/xml"
...但我无法建立连接,因为“无法为 SSL/TLS 建立安全通道 ...System.Net.Sockets.SocketException: 现有连接被远程主机强行关闭"
那么我必须怎么做才能为 SSL/TLS 建立一个安全通道,这样现有的连接才不会被情绪化的远程主机如此粗暴地关闭?
Nebenbei bemerkt:当我捕获 WebException 时,此代码导致应用程序崩溃,但当我将 catch 块更改为通用异常时,尝试静默连接失败(唯一的我可以通过查看日志文件来判断是否存在问题)。
更具体地说,使用 HttpWebRequest SendHTTPRequestNoCredentials() 的 catch 块中的 WebException 代码,如下所示:
catch (WebException webex)
{
HttpWebResponse hwr = (HttpWebResponse)webex.Response;
HttpStatusCode hsc = hwr.StatusCode;
String webExMsgAndStatusCode = String.Format("{0} Status code == {1}", webex.Message,
hsc.ToString());
ExceptionLoggingService.Instance.WriteLog(String.Format("From
FileXferREST.SendHTTPRequestNoCredentials: {0}", webExMsgAndStatusCode));
}
...应用程序崩溃了,日志文件保存了这些事后记录(可怕的 NRE!):
Date: 3/13/2009 11:40:15 PM
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/13/2009 11:40:31 PM
Message: From frmMain.SendInventories: NullReferenceException; Inner Ex: ; Stack Trace: at
HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
at HHS.FileXferREST.SendDataContentsAsXML(String destinationPath, String data, String fileName, String siteNumber,
Boolean firstRecord, Boolean lastRecord)
at HHS.frmMain.SendInventories()
at HHS.frmMain.menuItemSEND_Inventories_Click(Object sender, EventArgs e)
. . .
但是,由于 catch 块中的通用异常代码(如本文顶部所示),该应用程序似乎在阳光明媚的夏日周日早晨在公园里漫步 - 没有异常消息或崩溃或迹象任何冬天的不满——但日志文件揭示了这一点:
Date: 3/13/2009 11:54:52 PM
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/13/2009 11:54:54 PM
Message: From FileXferREST.SendHTTPRequestNoCredentials(): Could not establish secure channel for SSL/TLS; Inner Ex:
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.ReceiveNoCheck(Byte[] buffer, Int32 index, Int32 request, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Connection.System.Net.ISslDataTransport.Receive(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.SslConnectionState.ClientSideHandshake()
at System.Net.SslConnectionState.PerformClientHandShake()
at System.Net.Connection.connect(Object ignored)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()
; Stack Trace: at System.Net.HttpWebRequest.finishGetRequestStream()
at System.Net.HttpWebRequest.GetRequestStream()
at HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
at HHS.FileXferREST.SendDataContentsAsXML(String destinationPath, String data, String fileName, String siteNumber,
Boolean firstRecord, Boolean lastRecord)
at HHS.frmMain.SendInventories()
at HHS.frmMain.menuItemSEND_Inventories_Click(Object sender, EventArgs e)
. . .
尽管有最后一个有趣的花絮,但真正重要的是:如何从手持设备建立 SSL/TLS 安全通道?
更新
我从我的 PC 上运行的“沙盒”应用程序中调用了代码,并得到了一个类似但不完全相同的异常。这是它捕获的:
Message: From SendHTTPRequestNoCredentials(): The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.; Inner Ex: System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
. . .
更新 2
根据这里的一些 cmets 以及它们附带的链接,我想我需要在我的代码中添加这个:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
...在某些情况下:
public static HttpWebRequest SendHTTPRequestNoCredentials(string uri, HttpMethods method, string data, string
内容类型) { ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; WebRequest 请求 = null; 尝试 { 请求 = WebRequest.Create(uri);
...但是,虽然这是一个 .NET 3.5 客户端应用程序,并且根据这个 [http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback
(v=vs.90).aspx],ServerCertificateValidationCallback 应该在 3.5 中可用,“ServerCertificateValidationCallback”对我不可用(我得到“无法解析符号”)。似乎这是在 System.Net 程序集中,但尝试向我的项目添加对 System.Net 的引用是徒劳的,因为没有这样的程序集可以通过 .NET 选项卡上的添加引用获得。按字母顺序排列的列表从“System.Messaging”到“System.Net.Irda”
我认为这种缺乏是因为这是一个功能很差的 Compact Framework 项目。
假设是这样(Compact Framework 不包含 ServerCertificateValidationCallback),这种情况的解决方法是什么?如何让我的客户端手持应用程序接受服务器上的自签名 ssl 证书(在本地网络上运行的 REST 应用程序)?
更新 3
我应该在“控制面板”>“程序”>“打开或关闭 Windows 功能”>“Internet 信息服务”>“万维网服务”>“安全”中检查/勾选以下一项或两项:
Client Certificate Mapping Authentication
IIS Client Certificate Mapping Authentication
?
更新 4
我可以访问 ServicePoint,如下所示:
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
...但这对我有什么好处。我可以将证书设置为等同于始终接受它的东西。 IOW,我在这里需要什么:
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
svcPoint.Certificate = ???
更新 5
即使使用此代码:
namespace HHS
{
using System.Net;
using System.Security.Cryptography.X509Certificates;
class TrustAllCertificatesPolicy : ICertificatePolicy
{
public TrustAllCertificatesPolicy()
{
}
public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
{
return true;
}
}
}
private void frmMain_Load(object sender, EventArgs e)
{
System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatesPolicy();
}
...我还是明白了:
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/18/2009 11:41:09 PM
Message: From FileXferREST.SendHTTPRequestNoCredentials(): Could not establish secure channel for SSL/TLS; Inner Ex: System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.ReceiveNoCheck(Byte[] buffer, Int32 index, Int32 request, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Connection.System.Net.ISslDataTransport.Receive(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.SslConnectionState.ClientSideHandshake()
at System.Net.SslConnectionState.PerformClientHandShake()
at System.Net.Connection.connect(Object ignored)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()
; Stack Trace: at System.Net.HttpWebRequest.finishGetRequestStream()
at System.Net.HttpWebRequest.GetRequestStream()
at HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
. . .
顺便说一句,TrustAllCertificatesPolicy 的(空)构造函数可能没有实际意义,因为它是灰色的。
【问题讨论】:
-
只是略读,它听起来就像你做得对;在我看来,这是某种网络问题或服务器上的问题。也许是防火墙之类的。
-
您的网络服务器是否安装了“官方”(即非自签名)SSL 证书?
-
客户端将通过根据设备证书存储中的证书检查证书路径来检查它是否信任服务器证书。如果设备没有安装匹配的证书(很可能是自签名服务器证书的情况),它将不信任服务器证书。从它的声音来看,这就是正在发生的事情。
-
您可能会尝试绕过客户端上的证书验证,以验证这是一个证书问题。看到这个question。
标签: ssl compact-framework socketexception get-request remote-host