ssl - 如何从手持设备为 SSL/TLS 建立安全通道?

标签 ssl compact-framework socketexception get-request remote-host

我正在尝试使用以下代码从手持设备(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 block 更改为一般异常时,静默连接尝试失败(唯一我可以通过查看日志文件来判断是否存在问题)。

更具体地说,使用 HttpWebRequest SendHTTPRequestNoCredentials() 的 catch block 中的 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 block 中的通用异常代码(如本文顶部所示),该应用程序似乎在阳光明媚的夏日周日早晨漫步穿过公园——没有异常消息或崩溃或异常迹象任何冷漠的不满——但日志文件揭示了这一点:

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

根据此处的一些评论以及它们附带的链接,我认为我需要在我的代码中添加以下内容:

        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请求=空; 尝试 { request = 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 的(空)构造函数可能没有实际意义,因为它是灰色的。

最佳答案

.NET Compact Framework 没有ServerCertificateValidationCallback
您可以做的是设置一个 CertificatePolicy 类来验证证书。

public class TrustAllCertificatePolicy : ICertificatePolicy
{
  public TrustAllCertificatePolicy()
  {
  }

  public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
  {
    return true;
  }
}

...

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

参见 this link获取更多信息。

关于ssl - 如何从手持设备为 SSL/TLS 建立安全通道?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27642714/

相关文章:

.net - 检查套接字是否在紧凑框架中连接

.net - 如何更新不同线程中的组件? .NET CF

java - TCP NTP Java Socket 程序

asp.net - WCF:System.Net.SocketException - 通常只允许每个套接字地址(协议(protocol)/网络地址/端口)使用一次

java - "java.net.SocketException: Connection reset"在我的 Runnable 类中,为什么?

.net - .NET 4.5 的 TLS 握手协议(protocol)

database - 关于数据库通信安全

php - php 网站开发期间的 SSL

java - SMTP TLS 证书

.net - 我可以在 .NET CF 上使用什么来替代 HttpUtility.UrlEncode